diff --git a/dist/icosa-viewer.js b/dist/icosa-viewer.js index 7f6e0a0..4a84ab1 100644 --- a/dist/icosa-viewer.js +++ b/dist/icosa-viewer.js @@ -1,7 +1,7 @@ /*! * Icosa Viewer * https://github.com/icosa-gallery/icosa-viewer - * Copyright (c) 2021 Icosa Gallery + * Copyright (c) 2021-2022 Icosa Gallery * Released under the Apache 2.0 Licence. */ (function (global, factory) { @@ -1460,10 +1460,10 @@ /** * @license - * Copyright 2010-2021 Three.js Authors + * Copyright 2010-2022 Three.js Authors * SPDX-License-Identifier: MIT */ - const REVISION = '128'; + const REVISION = '139'; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const CullFaceNone = 0; const CullFaceBack = 1; @@ -1521,7 +1521,6 @@ const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; - const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; @@ -1541,7 +1540,6 @@ const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; - const UnsignedShort565Type = 1019; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; @@ -1554,7 +1552,6 @@ const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; - const RGBIntegerFormat = 1032; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; @@ -1583,20 +1580,6 @@ const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; - const SRGB8_ALPHA8_ASTC_4x4_Format = 37840; - const SRGB8_ALPHA8_ASTC_5x4_Format = 37841; - const SRGB8_ALPHA8_ASTC_5x5_Format = 37842; - const SRGB8_ALPHA8_ASTC_6x5_Format = 37843; - const SRGB8_ALPHA8_ASTC_6x6_Format = 37844; - const SRGB8_ALPHA8_ASTC_8x5_Format = 37845; - const SRGB8_ALPHA8_ASTC_8x6_Format = 37846; - const SRGB8_ALPHA8_ASTC_8x8_Format = 37847; - const SRGB8_ALPHA8_ASTC_10x5_Format = 37848; - const SRGB8_ALPHA8_ASTC_10x6_Format = 37849; - const SRGB8_ALPHA8_ASTC_10x8_Format = 37850; - const SRGB8_ALPHA8_ASTC_10x10_Format = 37851; - const SRGB8_ALPHA8_ASTC_12x10_Format = 37852; - const SRGB8_ALPHA8_ASTC_12x12_Format = 37853; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; @@ -1613,16 +1596,12 @@ const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; - const GammaEncoding = 3007; - const RGBEEncoding = 3002; - const LogLuvEncoding = 3003; - const RGBM7Encoding = 3004; - const RGBM16Encoding = 3005; - const RGBDEncoding = 3006; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; + const SRGBColorSpace = 'srgb'; + const LinearSRGBColorSpace = 'srgb-linear'; const KeepStencilOp = 7680; const AlwaysStencilFunc = 519; @@ -1630,6 +1609,8 @@ const DynamicDrawUsage = 35048; const GLSL3 = '300 es'; + const _SRGBAFormat = 1035; // fallback for WebGL 1 + /** * https://github.com/mrdoob/eventdispatcher.js/ */ @@ -1741,8 +1722,8 @@ _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 ]; - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); + // .toLowerCase() here flattens concatenated strings to save heap memory space. + return uuid.toLowerCase(); } @@ -1752,7 +1733,7 @@ } - // compute euclidian modulo of m % n + // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { @@ -1774,11 +1755,11 @@ return ( value - x ) / ( y - x ); - } else { + } else { return 0; - } + } } @@ -1850,13 +1831,17 @@ // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { - if ( s !== undefined ) _seed = s % 2147483647; + if ( s !== undefined ) _seed = s; + + // Mulberry32 generator - // Park-Miller algorithm + let t = _seed += 0x6D2B79F5; - _seed = _seed * 16807 % 2147483647; + t = Math.imul( t ^ t >>> 15, t | 1 ); - return ( _seed - 1 ) / 2147483646; + t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); + + return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } @@ -1946,6 +1931,70 @@ } + function denormalize$1( value, array ) { + + switch ( array.constructor ) { + + case Float32Array: + + return value; + + case Uint16Array: + + return value / 65535.0; + + case Uint8Array: + + return value / 255.0; + + case Int16Array: + + return Math.max( value / 32767.0, - 1.0 ); + + case Int8Array: + + return Math.max( value / 127.0, - 1.0 ); + + default: + + throw new Error( 'Invalid component type.' ); + + } + + } + + function normalize( value, array ) { + + switch ( array.constructor ) { + + case Float32Array: + + return value; + + case Uint16Array: + + return Math.round( value * 65535.0 ); + + case Uint8Array: + + return Math.round( value * 255.0 ); + + case Int16Array: + + return Math.round( value * 32767.0 ); + + case Int8Array: + + return Math.round( value * 127.0 ); + + default: + + throw new Error( 'Invalid component type.' ); + + } + + } + var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, @@ -1969,7 +2018,9 @@ isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, - setQuaternionFromProperEuler: setQuaternionFromProperEuler + setQuaternionFromProperEuler: setQuaternionFromProperEuler, + normalize: normalize, + denormalize: denormalize$1 }); class Vector2 { @@ -2444,6 +2495,13 @@ } + *[ Symbol.iterator ]() { + + yield this.x; + yield this.y; + + } + } Vector2.prototype.isVector2 = true; @@ -2786,322 +2844,323 @@ Matrix3.prototype.isMatrix3 = true; - let _canvas; + function arrayNeedsUint32( array ) { - class ImageUtils { + // assumes larger values usually on last - static getDataURL( image ) { + for ( let i = array.length - 1; i >= 0; -- i ) { - if ( /^data:/i.test( image.src ) ) { + if ( array[ i ] > 65535 ) return true; - return image.src; + } - } + return false; - if ( typeof HTMLCanvasElement == 'undefined' ) { + } - return image.src; + function createElementNS( name ) { - } + return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); - let canvas; + } - if ( image instanceof HTMLCanvasElement ) { + function SRGBToLinear( c ) { - canvas = image; + return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - } else { + } - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + function LinearToSRGB( c ) { - _canvas.width = image.width; - _canvas.height = image.height; + return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - const context = _canvas.getContext( '2d' ); + } - if ( image instanceof ImageData ) { + // JavaScript RGB-to-RGB transforms, defined as + // FN[InputColorSpace][OutputColorSpace] callback functions. + const FN = { + [ SRGBColorSpace ]: { [ LinearSRGBColorSpace ]: SRGBToLinear }, + [ LinearSRGBColorSpace ]: { [ SRGBColorSpace ]: LinearToSRGB }, + }; - context.putImageData( image, 0, 0 ); + const ColorManagement = { - } else { + legacyMode: true, - context.drawImage( image, 0, 0, image.width, image.height ); + get workingColorSpace() { - } + return LinearSRGBColorSpace; - canvas = _canvas; + }, - } + set workingColorSpace( colorSpace ) { - if ( canvas.width > 2048 || canvas.height > 2048 ) { + console.warn( 'THREE.ColorManagement: .workingColorSpace is readonly.' ); - console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); + }, - return canvas.toDataURL( 'image/jpeg', 0.6 ); + convert: function ( color, sourceColorSpace, targetColorSpace ) { - } else { + if ( this.legacyMode || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { - return canvas.toDataURL( 'image/png' ); + return color; } - } + if ( FN[ sourceColorSpace ] && FN[ sourceColorSpace ][ targetColorSpace ] !== undefined ) { - } + const fn = FN[ sourceColorSpace ][ targetColorSpace ]; - let textureId = 0; + color.r = fn( color.r ); + color.g = fn( color.g ); + color.b = fn( color.b ); - class Texture extends EventDispatcher$1 { + return color; - constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { + } - super(); + throw new Error( 'Unsupported color space conversion.' ); - Object.defineProperty( this, 'id', { value: textureId ++ } ); + }, - this.uuid = generateUUID(); + fromWorkingColorSpace: function ( color, targetColorSpace ) { - this.name = ''; + return this.convert( color, this.workingColorSpace, targetColorSpace ); - this.image = image; - this.mipmaps = []; + }, - this.mapping = mapping; + toWorkingColorSpace: function ( color, sourceColorSpace ) { - this.wrapS = wrapS; - this.wrapT = wrapT; + return this.convert( color, sourceColorSpace, this.workingColorSpace ); - this.magFilter = magFilter; - this.minFilter = minFilter; + }, - this.anisotropy = anisotropy; + }; - this.format = format; - this.internalFormat = null; - this.type = type; + const _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 }; - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; + const _rgb = { r: 0, g: 0, b: 0 }; + const _hslA = { h: 0, s: 0, l: 0 }; + const _hslB = { h: 0, s: 0, l: 0 }; - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); + function hue2rgb( p, q, t ) { - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + 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; - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding; + } - this.version = 0; - this.onUpdate = null; + function toComponents( source, target ) { - } + target.r = source.r; + target.g = source.g; + target.b = source.b; - updateMatrix() { + return target; - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); + } - } + class Color { - clone() { + constructor( r, g, b ) { - return new this.constructor().copy( this ); + if ( g === undefined && b === undefined ) { - } + // r is THREE.Color, hex or string + return this.set( r ); - copy( source ) { + } - this.name = source.name; + return this.setRGB( r, g, b ); - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); + } - this.mapping = source.mapping; + set( value ) { - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + if ( value && value.isColor ) { - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + this.copy( value ); - this.anisotropy = source.anisotropy; + } else if ( typeof value === 'number' ) { - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; + this.setHex( value ); - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; + } else if ( typeof value === 'string' ) { - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); + this.setStyle( value ); - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; + } return this; } - toJSON( meta ) { - - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - const output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, + setScalar( scalar ) { - mapping: this.mapping, + this.r = scalar; + this.g = scalar; + this.b = scalar; - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, + return this; - wrap: [ this.wrapS, this.wrapT ], + } - format: this.format, - type: this.type, - encoding: this.encoding, + setHex( hex, colorSpace = SRGBColorSpace ) { - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, + hex = Math.floor( hex ); - flipY: this.flipY, + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment + ColorManagement.toWorkingColorSpace( this, colorSpace ); - }; + return this; - if ( this.image !== undefined ) { + } - // TODO: Move to THREE.Image + setRGB( r, g, b, colorSpace = LinearSRGBColorSpace ) { - const image = this.image; + this.r = r; + this.g = g; + this.b = b; - if ( image.uuid === undefined ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - image.uuid = generateUUID(); // UGH + return this; - } + } - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { + setHSL( h, s, l, colorSpace = LinearSRGBColorSpace ) { - let url; + // h,s,l ranges are in 0.0 - 1.0 + h = euclideanModulo( h, 1 ); + s = clamp( s, 0, 1 ); + l = clamp( l, 0, 1 ); - if ( Array.isArray( image ) ) { + if ( s === 0 ) { - // process array of images e.g. CubeTexture + this.r = this.g = this.b = l; - url = []; + } else { - for ( let i = 0, l = image.length; i < l; i ++ ) { + const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + const q = ( 2 * l ) - p; - // check cube texture with data textures + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); - if ( image[ i ].isDataTexture ) { + } - url.push( serializeImage( image[ i ].image ) ); + ColorManagement.toWorkingColorSpace( this, colorSpace ); - } else { + return this; - url.push( serializeImage( image[ i ] ) ); + } - } + setStyle( style, colorSpace = SRGBColorSpace ) { - } + function handleAlpha( string ) { - } else { + if ( string === undefined ) return; - // process single image + if ( parseFloat( string ) < 1 ) { - url = serializeImage( image ); + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - } + } - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; + } - } - output.image = image.uuid; + let m; - } + if ( m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec( style ) ) { - if ( ! isRootObject ) { + // rgb / hsl - meta.textures[ this.uuid ] = output; + let color; + const name = m[ 1 ]; + const components = m[ 2 ]; - } + switch ( name ) { - return output; + case 'rgb': + case 'rgba': - } + if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - dispose() { + // 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; - this.dispatchEvent( { type: 'dispose' } ); + ColorManagement.toWorkingColorSpace( this, colorSpace ); - } + handleAlpha( color[ 4 ] ); - transformUv( uv ) { + return this; - if ( this.mapping !== UVMapping ) return uv; + } - uv.applyMatrix3( this.matrix ); + if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - if ( uv.x < 0 || uv.x > 1 ) { + // 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; - switch ( this.wrapS ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - case RepeatWrapping: + handleAlpha( color[ 4 ] ); - uv.x = uv.x - Math.floor( uv.x ); - break; + return this; - case ClampToEdgeWrapping: + } - uv.x = uv.x < 0 ? 0 : 1; break; - case MirroredRepeatWrapping: + case 'hsl': + case 'hsla': - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - uv.x = Math.ceil( uv.x ) - uv.x; + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + const h = parseFloat( color[ 1 ] ) / 360; + const s = parseInt( color[ 2 ], 10 ) / 100; + const l = parseInt( color[ 3 ], 10 ) / 100; - } else { + handleAlpha( color[ 4 ] ); - uv.x = uv.x - Math.floor( uv.x ); + return this.setHSL( h, s, l, colorSpace ); } @@ -3109,819 +3168,695 @@ } - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: + } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { - uv.y = uv.y - Math.floor( uv.y ); - break; + // hex color - case ClampToEdgeWrapping: + const hex = m[ 1 ]; + const size = hex.length; - uv.y = uv.y < 0 ? 0 : 1; - break; + if ( size === 3 ) { - case MirroredRepeatWrapping: + // #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; - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - uv.y = Math.ceil( uv.y ) - uv.y; + return this; - } else { + } else if ( size === 6 ) { - uv.y = uv.y - Math.floor( uv.y ); + // #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; - } + ColorManagement.toWorkingColorSpace( this, colorSpace ); - break; + return this; } } - if ( this.flipY ) { + if ( style && style.length > 0 ) { - uv.y = 1 - uv.y; + return this.setColorName( style, colorSpace ); } - return uv; - - } - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; + return this; } - } - - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype.isTexture = true; - - function serializeImage( image ) { - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - // default images - - return ImageUtils.getDataURL( image ); - - } else { + setColorName( style, colorSpace = SRGBColorSpace ) { - if ( image.data ) { + // color keywords + const hex = _colorKeywords[ style.toLowerCase() ]; - // images of DataTexture + if ( hex !== undefined ) { - return { - data: Array.prototype.slice.call( image.data ), - width: image.width, - height: image.height, - type: image.data.constructor.name - }; + // red + this.setHex( hex, colorSpace ); } else { - console.warn( 'THREE.Texture: Unable to serialize Texture.' ); - return {}; + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); } - } - - } - - class Vector4 { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; + return this; } - get width() { + clone() { - return this.z; + return new this.constructor( this.r, this.g, this.b ); } - set width( value ) { - - this.z = value; - - } + copy( color ) { - get height() { + this.r = color.r; + this.g = color.g; + this.b = color.b; - return this.w; + return this; } - set height( value ) { + copySRGBToLinear( color ) { - this.w = value; + this.r = SRGBToLinear( color.r ); + this.g = SRGBToLinear( color.g ); + this.b = SRGBToLinear( color.b ); + + return this; } - set( x, y, z, w ) { + copyLinearToSRGB( color ) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + this.r = LinearToSRGB( color.r ); + this.g = LinearToSRGB( color.g ); + this.b = LinearToSRGB( color.b ); return this; } - setScalar( scalar ) { + convertSRGBToLinear() { - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; + this.copySRGBToLinear( this ); return this; } - setX( x ) { + convertLinearToSRGB() { - this.x = x; + this.copyLinearToSRGB( this ); return this; } - setY( y ) { + getHex( colorSpace = SRGBColorSpace ) { - this.y = y; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - return this; + return clamp( _rgb.r * 255, 0, 255 ) << 16 ^ clamp( _rgb.g * 255, 0, 255 ) << 8 ^ clamp( _rgb.b * 255, 0, 255 ) << 0; } - setZ( z ) { - - this.z = z; + getHexString( colorSpace = SRGBColorSpace ) { - return this; + return ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } - setW( w ) { + getHSL( target, colorSpace = LinearSRGBColorSpace ) { - this.w = w; + // h,s,l ranges are in 0.0 - 1.0 - return this; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - } + const r = _rgb.r, g = _rgb.g, b = _rgb.b; - setComponent( index, value ) { + const max = Math.max( r, g, b ); + const min = Math.min( r, g, b ); - switch ( index ) { + let hue, saturation; + const lightness = ( min + max ) / 2.0; - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); + if ( min === max ) { - } + hue = 0; + saturation = 0; - return this; + } else { - } + const delta = max - min; - getComponent( index ) { + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - switch ( index ) { + switch ( max ) { - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); + 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; - clone() { + } - return new this.constructor( this.x, this.y, this.z, this.w ); + target.h = hue; + target.s = saturation; + target.l = lightness; + + return target; } - copy( v ) { + getRGB( target, colorSpace = LinearSRGBColorSpace ) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - return this; + target.r = _rgb.r; + target.g = _rgb.g; + target.b = _rgb.b; + + return target; } - add( v, w ) { + getStyle( colorSpace = SRGBColorSpace ) { - if ( w !== undefined ) { + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + if ( colorSpace !== SRGBColorSpace ) { - } + // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). + return `color(${ colorSpace } ${ _rgb.r } ${ _rgb.g } ${ _rgb.b })`; - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; + } - return this; + return `rgb(${( _rgb.r * 255 ) | 0},${( _rgb.g * 255 ) | 0},${( _rgb.b * 255 ) | 0})`; } - addScalar( s ) { + offsetHSL( h, s, l ) { - this.x += s; - this.y += s; - this.z += s; - this.w += s; + this.getHSL( _hslA ); + + _hslA.h += h; _hslA.s += s; _hslA.l += l; + + this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } - addVectors( a, b ) { + add( color ) { - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; + this.r += color.r; + this.g += color.g; + this.b += color.b; return this; } - addScaledVector( v, s ) { + addColors( color1, color2 ) { - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; return this; } - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } + addScalar( s ) { - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; + this.r += s; + this.g += s; + this.b += s; return this; } - subScalar( s ) { + sub( color ) { - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; + 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; } - subVectors( a, b ) { + multiply( color ) { - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; return this; } - multiply( v ) { + multiplyScalar( s ) { - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - this.w *= v.w; + this.r *= s; + this.g *= s; + this.b *= s; return this; } - multiplyScalar( scalar ) { + lerp( color, alpha ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; return this; } - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z, w = this.w; - const e = m.elements; + lerpColors( color1, color2, alpha ) { - 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; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + this.r = color1.r + ( color2.r - color1.r ) * alpha; + this.g = color1.g + ( color2.g - color1.g ) * alpha; + this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } + lerpHSL( color, alpha ) { - setAxisAngleFromQuaternion( q ) { + this.getHSL( _hslA ); + color.getHSL( _hslB ); - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + const h = lerp( _hslA.h, _hslB.h, alpha ); + const s = lerp( _hslA.s, _hslB.s, alpha ); + const l = lerp( _hslA.l, _hslB.l, alpha ); - // q is assumed to be normalized + this.setHSL( h, s, l ); - this.w = 2 * Math.acos( q.w ); + return this; - const s = Math.sqrt( 1 - q.w * q.w ); + } - if ( s < 0.0001 ) { + equals( c ) { - this.x = 1; - this.y = 0; - this.z = 0; + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - } else { + } - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; + fromArray( array, offset = 0 ) { - } + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; return this; } - setAxisAngleFromRotationMatrix( m ) { + toArray( array = [], offset = 0 ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + array[ offset ] = this.r; + array[ offset + 1 ] = this.g; + array[ offset + 2 ] = this.b; - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + return array; - let angle, x, y, z; // variables for result - const epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + } - te = m.elements, + fromBufferAttribute( attribute, index ) { - 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 ]; + this.r = attribute.getX( index ); + this.g = attribute.getY( index ); + this.b = attribute.getZ( index ); - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { + if ( attribute.normalized === true ) { - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms + // assuming Uint8Array - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + this.r /= 255; + this.g /= 255; + this.b /= 255; - // this singularity is identity matrix so angle = 0 + } - this.set( 1, 0, 0, 0 ); + return this; - return this; // zero angle, arbitrary axis + } - } + toJSON() { - // otherwise this singularity is angle = 180 + return this.getHex(); - angle = Math.PI; + } - const xx = ( m11 + 1 ) / 2; - const yy = ( m22 + 1 ) / 2; - const zz = ( m33 + 1 ) / 2; - const xy = ( m12 + m21 ) / 4; - const xz = ( m13 + m31 ) / 4; - const yz = ( m23 + m32 ) / 4; + } - if ( ( xx > yy ) && ( xx > zz ) ) { + Color.NAMES = _colorKeywords; - // m11 is the largest diagonal term + Color.prototype.isColor = true; + Color.prototype.r = 1; + Color.prototype.g = 1; + Color.prototype.b = 1; - if ( xx < epsilon ) { + let _canvas; - x = 0; - y = 0.707106781; - z = 0.707106781; + class ImageUtils { - } else { + static getDataURL( image ) { - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; + if ( /^data:/i.test( image.src ) ) { - } + return image.src; - } else if ( yy > zz ) { + } - // m22 is the largest diagonal term + if ( typeof HTMLCanvasElement == 'undefined' ) { - if ( yy < epsilon ) { + return image.src; - x = 0.707106781; - y = 0; - z = 0.707106781; + } - } else { + let canvas; - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; + if ( image instanceof HTMLCanvasElement ) { - } + canvas = image; - } else { + } else { - // m33 is the largest diagonal term so base result on this + if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' ); - if ( zz < epsilon ) { + _canvas.width = image.width; + _canvas.height = image.height; - x = 0.707106781; - y = 0.707106781; - z = 0; + const context = _canvas.getContext( '2d' ); - } else { + if ( image instanceof ImageData ) { - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; + context.putImageData( image, 0, 0 ); - } + } else { - } + context.drawImage( image, 0, 0, image.width, image.height ); - this.set( x, y, z, angle ); + } - return this; // return 180 deg rotation + canvas = _canvas; } - // as we have reached here there are no singularities so we can handle normally + if ( canvas.width > 2048 || canvas.height > 2048 ) { - let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); - if ( Math.abs( s ) < 0.001 ) s = 1; + return canvas.toDataURL( 'image/jpeg', 0.6 ); - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case + } else { - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + return canvas.toDataURL( 'image/png' ); - return this; + } } - min( v ) { + static sRGBToLinear( image ) { - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - return this; + const canvas = createElementNS( 'canvas' ); - } + canvas.width = image.width; + canvas.height = image.height; - max( v ) { + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height ); - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); + const imageData = context.getImageData( 0, 0, image.width, image.height ); + const data = imageData.data; - return this; + for ( let i = 0; i < data.length; i ++ ) { - } + data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; - clamp( min, max ) { + } - // assumes min < max, componentwise + context.putImageData( imageData, 0, 0 ); - 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 ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); + return canvas; - return this; + } else if ( image.data ) { - } + const data = image.data.slice( 0 ); - clampScalar( minVal, maxVal ) { + for ( let i = 0; i < data.length; i ++ ) { - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); + if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { - return this; + data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); - } + } else { - clampLength( min, max ) { + // assuming float - const length = this.length(); + data[ i ] = SRGBToLinear( data[ i ] ); - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + } - } + } - floor() { + return { + data: data, + width: image.width, + height: image.height + }; - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); + } else { - return this; + console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); + return image; - } + } - ceil() { + } - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); + } - return this; + class Source { - } + constructor( data = null ) { - round() { + this.uuid = generateUUID(); - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); + this.data = data; - return this; + this.version = 0; } - roundToZero() { - - 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 ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + set needsUpdate( value ) { - return this; + if ( value === true ) this.version ++; } - negate() { + toJSON( meta ) { - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - return this; + if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { - } + return meta.images[ this.uuid ]; - dot( v ) { + } - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + const output = { + uuid: this.uuid, + url: '' + }; - } + const data = this.data; - lengthSq() { + if ( data !== null ) { - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + let url; - } + if ( Array.isArray( data ) ) { - length() { + // cube texture - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + url = []; - } + for ( let i = 0, l = data.length; i < l; i ++ ) { - manhattanLength() { + if ( data[ i ].isDataTexture ) { - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + url.push( serializeImage( data[ i ].image ) ); - } + } else { - normalize() { + url.push( serializeImage( data[ i ] ) ); - return this.divideScalar( this.length() || 1 ); + } - } + } - setLength( length ) { + } else { - return this.normalize().multiplyScalar( length ); + // texture - } + url = serializeImage( data ); - lerp( v, alpha ) { + } - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; + output.url = url; - return this; + } - } + if ( ! isRootObject ) { - lerpVectors( v1, v2, alpha ) { + meta.images[ this.uuid ] = output; - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; + } - return this; + return output; } - equals( v ) { + } - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + function serializeImage( image ) { - } + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - fromArray( array, offset = 0 ) { + // default images - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; + return ImageUtils.getDataURL( image ); - return this; + } else { - } + if ( image.data ) { - toArray( array = [], offset = 0 ) { + // images of DataTexture - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; + return { + data: Array.prototype.slice.call( image.data ), + width: image.width, + height: image.height, + type: image.data.constructor.name + }; - return array; + } else { - } + console.warn( 'THREE.Texture: Unable to serialize Texture.' ); + return {}; - fromBufferAttribute( attribute, index, offset ) { + } - if ( offset !== undefined ) { + } - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); + } - } + Source.prototype.isSource = true; - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); + let textureId = 0; - return this; + class Texture extends EventDispatcher$1 { - } + constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { - random() { + super(); - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); + Object.defineProperty( this, 'id', { value: textureId ++ } ); - return this; + this.uuid = generateUUID(); - } + this.name = ''; - } + this.source = new Source( image ); + this.mipmaps = []; - Vector4.prototype.isVector4 = true; + this.mapping = mapping; - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - class WebGLRenderTarget extends EventDispatcher$1 { + this.wrapS = wrapS; + this.wrapT = wrapT; - constructor( width, height, options ) { + this.magFilter = magFilter; + this.minFilter = minFilter; - super(); + this.anisotropy = anisotropy; - this.width = width; - this.height = height; - this.depth = 1; + this.format = format; + this.internalFormat = null; + this.type = type; - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; + this.offset = new Vector2( 0, 0 ); + this.repeat = new Vector2( 1, 1 ); + this.center = new Vector2( 0, 0 ); + this.rotation = 0; - this.viewport = new Vector4( 0, 0, width, height ); + this.matrixAutoUpdate = true; + this.matrix = new Matrix3(); - options = options || {}; + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + this.encoding = encoding; - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; - this.texture.image.depth = 1; + this.userData = {}; - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; + this.version = 0; + this.onUpdate = null; - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; + this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not + this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } - setTexture( texture ) { - - texture.image = { - width: this.width, - height: this.height, - depth: this.depth - }; + get image() { - this.texture = texture; + return this.source.data; } - setSize( width, height, depth = 1 ) { - - if ( this.width !== width || this.height !== height || this.depth !== depth ) { - - this.width = width; - this.height = height; - this.depth = depth; + set image( value ) { - this.texture.image.width = width; - this.texture.image.height = height; - this.texture.image.depth = depth; + this.source.data = value; - this.dispose(); + } - } + updateMatrix() { - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); + this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } @@ -3933,2711 +3868,2693 @@ copy( source ) { - this.width = source.width; - this.height = source.height; - this.depth = source.depth; + this.name = source.name; - this.viewport.copy( source.viewport ); + this.source = source.source; + this.mipmaps = source.mipmaps.slice( 0 ); - this.texture = source.texture.clone(); + this.mapping = source.mapping; - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; + + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; + + this.anisotropy = source.anisotropy; + + this.format = source.format; + this.internalFormat = source.internalFormat; + this.type = source.type; + + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + this.center.copy( source.center ); + this.rotation = source.rotation; + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrix.copy( source.matrix ); + + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + this.needsUpdate = true; return this; } - dispose() { + toJSON( meta ) { - this.dispatchEvent( { type: 'dispose' } ); + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - } + if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - } + return meta.textures[ this.uuid ]; - WebGLRenderTarget.prototype.isWebGLRenderTarget = true; + } - class WebGLMultisampleRenderTarget extends WebGLRenderTarget { + const output = { - constructor( width, height, options ) { + metadata: { + version: 4.5, + type: 'Texture', + generator: 'Texture.toJSON' + }, - super( width, height, options ); + uuid: this.uuid, + name: this.name, - this.samples = 4; + image: this.source.toJSON( meta ).uuid, - } + mapping: this.mapping, - copy( source ) { + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + center: [ this.center.x, this.center.y ], + rotation: this.rotation, - super.copy.call( this, source ); + wrap: [ this.wrapS, this.wrapT ], - this.samples = source.samples; + format: this.format, + type: this.type, + encoding: this.encoding, - return this; + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, - } + flipY: this.flipY, - } + premultiplyAlpha: this.premultiplyAlpha, + unpackAlignment: this.unpackAlignment - WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; + }; - class Quaternion { + if ( JSON.stringify( this.userData ) !== '{}' ) output.userData = this.userData; - constructor( x = 0, y = 0, z = 0, w = 1 ) { + if ( ! isRootObject ) { - this._x = x; - this._y = y; - this._z = z; - this._w = w; + meta.textures[ this.uuid ] = output; + + } + + return output; } - static slerp( qa, qb, qm, t ) { + dispose() { - console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); - return qm.slerpQuaternions( qa, qb, t ); + this.dispatchEvent( { type: 'dispose' } ); } - static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { + transformUv( uv ) { - // fuzz-free, array-based Quaternion SLERP operation + if ( this.mapping !== UVMapping ) return uv; - let x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ]; + uv.applyMatrix3( this.matrix ); - const x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; + if ( uv.x < 0 || uv.x > 1 ) { - if ( t === 0 ) { + switch ( this.wrapS ) { - dst[ dstOffset + 0 ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - return; + case RepeatWrapping: - } + uv.x = uv.x - Math.floor( uv.x ); + break; - if ( t === 1 ) { + case ClampToEdgeWrapping: - dst[ dstOffset + 0 ] = x1; - dst[ dstOffset + 1 ] = y1; - dst[ dstOffset + 2 ] = z1; - dst[ dstOffset + 3 ] = w1; - return; + uv.x = uv.x < 0 ? 0 : 1; + break; - } + case MirroredRepeatWrapping: - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; + uv.x = Math.ceil( uv.x ) - uv.x; - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { + } else { - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); + uv.x = uv.x - Math.floor( uv.x ); - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; + } + + break; } - const tDir = t * dir; + } - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; + if ( uv.y < 0 || uv.y > 1 ) { - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { + switch ( this.wrapT ) { - const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); + case RepeatWrapping: - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; + uv.y = uv.y - Math.floor( uv.y ); + break; - } + case ClampToEdgeWrapping: - } + uv.y = uv.y < 0 ? 0 : 1; + break; - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; + case MirroredRepeatWrapping: - } + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { + uv.y = Math.ceil( uv.y ) - uv.y; - const x0 = src0[ srcOffset0 ]; - const y0 = src0[ srcOffset0 + 1 ]; - const z0 = src0[ srcOffset0 + 2 ]; - const w0 = src0[ srcOffset0 + 3 ]; + } else { - const x1 = src1[ srcOffset1 ]; - const y1 = src1[ srcOffset1 + 1 ]; - const z1 = src1[ srcOffset1 + 2 ]; - const w1 = src1[ srcOffset1 + 3 ]; + uv.y = uv.y - Math.floor( uv.y ); - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; + } - return dst; + break; - } + } - get x() { + } - return this._x; + if ( this.flipY ) { - } + uv.y = 1 - uv.y; - set x( value ) { + } - this._x = value; - this._onChangeCallback(); + return uv; } - get y() { + set needsUpdate( value ) { - return this._y; + if ( value === true ) { + + this.version ++; + this.source.needsUpdate = true; + + } } - set y( value ) { + } - this._y = value; - this._onChangeCallback(); + Texture.DEFAULT_IMAGE = null; + Texture.DEFAULT_MAPPING = UVMapping; + + Texture.prototype.isTexture = true; + + class Vector4 { + + constructor( x = 0, y = 0, z = 0, w = 1 ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; } - get z() { + get width() { - return this._z; + return this.z; } - set z( value ) { + set width( value ) { - this._z = value; - this._onChangeCallback(); + this.z = value; } - get w() { + get height() { - return this._w; + return this.w; } - set w( value ) { + set height( value ) { - this._w = value; - this._onChangeCallback(); + this.w = value; } set( x, y, z, w ) { - this._x = x; - this._y = y; - this._z = z; - this._w = w; - - this._onChangeCallback(); + this.x = x; + this.y = y; + this.z = z; + this.w = w; return this; } - clone() { + setScalar( scalar ) { - return new this.constructor( this._x, this._y, this._z, this._w ); + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; - } + return this; - copy( quaternion ) { + } - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; + setX( x ) { - this._onChangeCallback(); + this.x = x; return this; } - setFromEuler( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { - - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + setY( y ) { - } + this.y = y; - const x = euler._x, y = euler._y, z = euler._z, order = euler._order; + return this; - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m + } - const cos = Math.cos; - const sin = Math.sin; + setZ( z ) { - const c1 = cos( x / 2 ); - const c2 = cos( y / 2 ); - const c3 = cos( z / 2 ); + this.z = z; - const s1 = sin( x / 2 ); - const s2 = sin( y / 2 ); - const s3 = sin( z / 2 ); + return this; - switch ( order ) { + } - case '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; - break; + setW( w ) { - case '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; - break; + this.w = w; - case '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; - break; + return this; - case '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; - break; + } - case '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; - break; + setComponent( index, value ) { - case '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; - break; + switch ( index ) { - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); } - if ( update !== false ) this._onChangeCallback(); - return this; } - setFromAxisAngle( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized - - const halfAngle = angle / 2, s = Math.sin( halfAngle ); + getComponent( index ) { - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); + switch ( index ) { - this._onChangeCallback(); + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); - return this; + } } - setFromRotationMatrix( m ) { + clone() { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + return new this.constructor( this.x, this.y, this.z, this.w ); - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + } - const te = m.elements, + copy( v ) { - 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 ], + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; - trace = m11 + m22 + m33; + return this; - if ( trace > 0 ) { + } - const s = 0.5 / Math.sqrt( trace + 1.0 ); + add( v, w ) { - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; + if ( w !== undefined ) { - } else if ( m11 > m22 && m11 > m33 ) { + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); - const 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; + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; - } else if ( m22 > m33 ) { + return this; - const 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; + addScalar( s ) { - } else { + this.x += s; + this.y += s; + this.z += s; + this.w += s; - const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + return this; - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; + } - } + addVectors( a, b ) { - this._onChangeCallback(); + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; return this; } - setFromUnitVectors( vFrom, vTo ) { - - // assumes direction vectors vFrom and vTo are normalized - - let r = vFrom.dot( vTo ) + 1; - - if ( r < Number.EPSILON ) { - - // vFrom and vTo point in opposite directions - - r = 0; - - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; + addScaledVector( v, s ) { - } else { + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; + return this; - } + } - } else { + sub( v, w ) { - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 + if ( w !== undefined ) { - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); } - return this.normalize(); - - } - - angleTo( q ) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; - return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); + return this; } - rotateTowards( q, step ) { - - const angle = this.angleTo( q ); - - if ( angle === 0 ) return this; - - const t = Math.min( 1, step / angle ); + subScalar( s ) { - this.slerp( q, t ); + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; return this; } - identity() { + subVectors( a, b ) { - return this.set( 0, 0, 0, 1 ); + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; } - invert() { + multiply( v ) { - // quaternion is assumed to have unit length + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + this.w *= v.w; - return this.conjugate(); + return this; } - conjugate() { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; + multiplyScalar( scalar ) { - this._onChangeCallback(); + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; return this; } - dot( v ) { + applyMatrix4( m ) { - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + const x = this.x, y = this.y, z = this.z, w = this.w; + const e = m.elements; + + 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; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; } - lengthSq() { + divideScalar( scalar ) { - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + return this.multiplyScalar( 1 / scalar ); } - length() { + setAxisAngleFromQuaternion( q ) { - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - } + // q is assumed to be normalized - normalize() { + this.w = 2 * Math.acos( q.w ); - let l = this.length(); + const s = Math.sqrt( 1 - q.w * q.w ); - if ( l === 0 ) { + if ( s < 0.0001 ) { - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; + this.x = 1; + this.y = 0; + this.z = 0; } 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.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; } - this._onChangeCallback(); - return this; } - multiply( q, p ) { + setAxisAngleFromRotationMatrix( m ) { - if ( p !== undefined ) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - } + let angle, x, y, z; // variables for result + const epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - return this.multiplyQuaternions( this, q ); + 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 ]; - premultiply( q ) { + if ( ( Math.abs( m12 - m21 ) < epsilon ) && + ( Math.abs( m13 - m31 ) < epsilon ) && + ( Math.abs( m23 - m32 ) < epsilon ) ) { - return this.multiplyQuaternions( q, this ); + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms - } + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && + ( Math.abs( m13 + m31 ) < epsilon2 ) && + ( Math.abs( m23 + m32 ) < epsilon2 ) && + ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - multiplyQuaternions( a, b ) { + // this singularity is identity matrix so angle = 0 - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + this.set( 1, 0, 0, 0 ); - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + return this; // zero angle, arbitrary axis - 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(); + // otherwise this singularity is angle = 180 - return this; + angle = Math.PI; - } + const xx = ( m11 + 1 ) / 2; + const yy = ( m22 + 1 ) / 2; + const zz = ( m33 + 1 ) / 2; + const xy = ( m12 + m21 ) / 4; + const xz = ( m13 + m31 ) / 4; + const yz = ( m23 + m32 ) / 4; - slerp( qb, t ) { + if ( ( xx > yy ) && ( xx > zz ) ) { - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); + // m11 is the largest diagonal term - const x = this._x, y = this._y, z = this._z, w = this._w; + if ( xx < epsilon ) { - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + x = 0; + y = 0.707106781; + z = 0.707106781; - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + } else { - if ( cosHalfTheta < 0 ) { + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; + } - cosHalfTheta = - cosHalfTheta; + } else if ( yy > zz ) { - } else { + // m22 is the largest diagonal term - this.copy( qb ); + if ( yy < epsilon ) { - } + x = 0.707106781; + y = 0; + z = 0.707106781; - if ( cosHalfTheta >= 1.0 ) { + } else { - this._w = w; - this._x = x; - this._y = y; - this._z = z; + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; - return this; + } - } + } else { - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + // m33 is the largest diagonal term so base result on this - if ( sqrSinHalfTheta <= Number.EPSILON ) { + if ( zz < epsilon ) { - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; + x = 0.707106781; + y = 0.707106781; + z = 0; - this.normalize(); - this._onChangeCallback(); + } else { - return this; + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; - } + } - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const 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.set( x, y, z, angle ); - this._onChangeCallback(); + return this; // return 180 deg rotation - return this; + } - } + // as we have reached here there are no singularities so we can handle normally - slerpQuaternions( qa, qb, t ) { + let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - this.copy( qa ).slerp( qb, t ); + if ( Math.abs( s ) < 0.001 ) s = 1; - } + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case - equals( quaternion ) { + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + return this; } - fromArray( array, offset = 0 ) { - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; + min( v ) { - this._onChangeCallback(); + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); return this; } - toArray( array = [], offset = 0 ) { + max( v ) { - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); - return array; + return this; } - fromBufferAttribute( attribute, index ) { + clamp( min, max ) { - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); + // 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 ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } - _onChange( callback ) { + clampScalar( minVal, maxVal ) { - this._onChangeCallback = callback; + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); + this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } - _onChangeCallback() {} - - } - - Quaternion.prototype.isQuaternion = true; - - class Vector3 { + clampLength( min, max ) { - constructor( x = 0, y = 0, z = 0 ) { + const length = this.length(); - this.x = x; - this.y = y; - this.z = z; + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } - set( x, y, z ) { - - if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) + floor() { - this.x = x; - this.y = y; - this.z = z; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); return this; } - setScalar( scalar ) { + ceil() { - this.x = scalar; - this.y = scalar; - this.z = scalar; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); return this; } - setX( x ) { + round() { - this.x = x; + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); return this; } - setY( y ) { + roundToZero() { - this.y = y; + 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 ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } - setZ( z ) { + negate() { - this.z = z; + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; return this; } - setComponent( index, value ) { + dot( v ) { - switch ( index ) { + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - 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 ); + } - } + lengthSq() { - return this; + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } - getComponent( 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 ); + length() { - } + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } - clone() { + manhattanLength() { - return new this.constructor( this.x, this.y, this.z ); + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; + normalize() { - return this; + return this.divideScalar( this.length() || 1 ); } - add( v, w ) { + setLength( length ) { - if ( w !== undefined ) { + return this.normalize().multiplyScalar( length ); - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + } - } + lerp( v, alpha ) { - this.x += v.x; - this.y += v.y; - this.z += v.z; + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; return this; } - addScalar( s ) { + lerpVectors( v1, v2, alpha ) { - this.x += s; - this.y += s; - this.z += s; + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; + this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; + equals( v ) { - return this; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } - addScaledVector( v, s ) { + fromArray( array, offset = 0 ) { - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; return this; } - sub( v, w ) { + toArray( array = [], offset = 0 ) { - if ( w !== undefined ) { + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + return array; - } + } - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; + fromBufferAttribute( attribute, index, offset ) { - return this; + if ( offset !== undefined ) { - } + console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - subScalar( s ) { + } - this.x -= s; - this.y -= s; - this.z -= s; + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); + this.w = attribute.getW( index ); return this; } - subVectors( a, b ) { + random() { - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); + this.w = Math.random(); return this; } - multiply( v, w ) { + *[ Symbol.iterator ]() { - if ( w !== undefined ) { + yield this.x; + yield this.y; + yield this.z; + yield this.w; - console.warn( 'THREE.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( scalar ) { + Vector4.prototype.isVector4 = true; - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; + /* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers + */ + class WebGLRenderTarget extends EventDispatcher$1 { - return this; + constructor( width, height, options = {} ) { - } + super(); - multiplyVectors( a, b ) { + this.width = width; + this.height = height; + this.depth = 1; - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; + this.scissor = new Vector4( 0, 0, width, height ); + this.scissorTest = false; - return this; + this.viewport = new Vector4( 0, 0, width, height ); - } + const image = { width: width, height: height, depth: 1 }; - applyEuler( euler ) { + this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; - if ( ! ( euler && euler.isEuler ) ) { + this.texture.flipY = false; + this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; + this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - } + this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); + this.samples = options.samples !== undefined ? options.samples : 0; } - applyAxisAngle( axis, angle ) { + setSize( width, height, depth = 1 ) { - return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - } + this.width = width; + this.height = height; + this.depth = depth; - applyMatrix3( m ) { + this.texture.image.width = width; + this.texture.image.height = height; + this.texture.image.depth = depth; - const x = this.x, y = this.y, z = this.z; - const e = m.elements; + this.dispose(); - 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; + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); } - applyNormalMatrix( m ) { + clone() { - return this.applyMatrix3( m ).normalize(); + return new this.constructor().copy( this ); } - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - const 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; + copy( source ) { - return this; + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - } + this.viewport.copy( source.viewport ); - applyQuaternion( q ) { + this.texture = source.texture.clone(); + this.texture.isRenderTargetTexture = true; - const x = this.x, y = this.y, z = this.z; - const qx = q.x, qy = q.y, qz = q.z, qw = q.w; + // ensure image object is not shared, see #20328 - // calculate quat * vector + this.texture.image = Object.assign( {}, source.texture.image ); - const ix = qw * x + qy * z - qz * y; - const iy = qw * y + qz * x - qx * z; - const iz = qw * z + qx * y - qy * x; - const iw = - qx * x - qy * y - qz * z; + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - // calculate result * inverse quat + if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); - 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; + this.samples = source.samples; return this; } - project( camera ) { + dispose() { - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); + this.dispatchEvent( { type: 'dispose' } ); } - unproject( camera ) { + } - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); + WebGLRenderTarget.prototype.isWebGLRenderTarget = true; - } + class DataArrayTexture extends Texture { - transformDirection( m ) { + constructor( data = null, width = 1, height = 1, depth = 1 ) { - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction + super( null ); - const x = this.x, y = this.y, z = this.z; - const e = m.elements; + this.image = { data, width, height, depth }; - 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; + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - return this.normalize(); + this.wrapR = ClampToEdgeWrapping; + + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; } - divide( v ) { + } - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; + DataArrayTexture.prototype.isDataArrayTexture = true; - return this; + class WebGLArrayRenderTarget extends WebGLRenderTarget { - } + constructor( width, height, depth ) { - divideScalar( scalar ) { + super( width, height ); - return this.multiplyScalar( 1 / scalar ); + this.depth = depth; - } + this.texture = new DataArrayTexture( null, width, height, depth ); - min( v ) { + this.texture.isRenderTargetTexture = true; - 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; + } - } + WebGLArrayRenderTarget.prototype.isWebGLArrayRenderTarget = true; - max( v ) { + class Data3DTexture extends Texture { - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); + constructor( data = null, width = 1, height = 1, depth = 1 ) { - return this; + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // const texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 - } + super( null ); - clamp( min, max ) { + this.image = { data, width, height, depth }; - // assumes min < max, componentwise + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - 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 ) ); + this.wrapR = ClampToEdgeWrapping; - return this; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; } - clampScalar( minVal, maxVal ) { + } - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); + Data3DTexture.prototype.isData3DTexture = true; - return this; + class WebGL3DRenderTarget extends WebGLRenderTarget { - } + constructor( width, height, depth ) { - clampLength( min, max ) { + super( width, height ); - const length = this.length(); + this.depth = depth; - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + this.texture = new Data3DTexture( null, width, height, depth ); - } + this.texture.isRenderTargetTexture = true; - floor() { + } - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); + } - return this; + WebGL3DRenderTarget.prototype.isWebGL3DRenderTarget = true; - } + class WebGLMultipleRenderTargets extends WebGLRenderTarget { - ceil() { + constructor( width, height, count, options = {} ) { - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); + super( width, height, options ); - return this; + const texture = this.texture; - } + this.texture = []; - round() { + for ( let i = 0; i < count; i ++ ) { - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); + this.texture[ i ] = texture.clone(); + this.texture[ i ].isRenderTargetTexture = true; - return this; + } } - roundToZero() { + setSize( width, height, depth = 1 ) { - 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 ); + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - return this; + this.width = width; + this.height = height; + this.depth = depth; - } + for ( let i = 0, il = this.texture.length; i < il; i ++ ) { - negate() { + this.texture[ i ].image.width = width; + this.texture[ i ].image.height = height; + this.texture[ i ].image.depth = depth; - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; + } - return this; + this.dispose(); - } + } - dot( v ) { + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - return this.x * v.x + this.y * v.y + this.z * v.z; + return this; } - // TODO lengthSquared? - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z; + copy( source ) { - } + this.dispose(); - length() { + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + this.viewport.set( 0, 0, this.width, this.height ); + this.scissor.set( 0, 0, this.width, this.height ); - } + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; - manhattanLength() { + this.texture.length = 0; - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + for ( let i = 0, il = source.texture.length; i < il; i ++ ) { - } + this.texture[ i ] = source.texture[ i ].clone(); - normalize() { + } - return this.divideScalar( this.length() || 1 ); + return this; } - setLength( length ) { - - return this.normalize().multiplyScalar( length ); + } - } + WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; - lerp( v, alpha ) { + class Quaternion { - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; + constructor( x = 0, y = 0, z = 0, w = 1 ) { - return this; + this._x = x; + this._y = y; + this._z = z; + this._w = w; } - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; + static slerp( qa, qb, qm, t ) { - return this; + console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); + return qm.slerpQuaternions( qa, qb, t ); } - cross( v, w ) { + static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - if ( w !== undefined ) { + // fuzz-free, array-based Quaternion SLERP operation - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); + let x0 = src0[ srcOffset0 + 0 ], + y0 = src0[ srcOffset0 + 1 ], + z0 = src0[ srcOffset0 + 2 ], + w0 = src0[ srcOffset0 + 3 ]; - } + const x1 = src1[ srcOffset1 + 0 ], + y1 = src1[ srcOffset1 + 1 ], + z1 = src1[ srcOffset1 + 2 ], + w1 = src1[ srcOffset1 + 3 ]; - return this.crossVectors( this, v ); + if ( t === 0 ) { - } + dst[ dstOffset + 0 ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; + return; - crossVectors( a, b ) { + } - const ax = a.x, ay = a.y, az = a.z; - const bx = b.x, by = b.y, bz = b.z; + if ( t === 1 ) { - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; + dst[ dstOffset + 0 ] = x1; + dst[ dstOffset + 1 ] = y1; + dst[ dstOffset + 2 ] = z1; + dst[ dstOffset + 3 ] = w1; + return; - return this; + } - } + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - projectOnVector( v ) { + let s = 1 - t; + const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + dir = ( cos >= 0 ? 1 : - 1 ), + sqrSin = 1 - cos * cos; - const denominator = v.lengthSq(); + // Skip the Slerp for tiny steps to avoid numeric problems: + if ( sqrSin > Number.EPSILON ) { - if ( denominator === 0 ) return this.set( 0, 0, 0 ); + const sin = Math.sqrt( sqrSin ), + len = Math.atan2( sin, cos * dir ); - const scalar = v.dot( this ) / denominator; + s = Math.sin( s * len ) / sin; + t = Math.sin( t * len ) / sin; - return this.copy( v ).multiplyScalar( scalar ); + } - } + const tDir = t * dir; - projectOnPlane( planeNormal ) { + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; - _vector$c.copy( this ).projectOnVector( planeNormal ); + // Normalize in case we just did a lerp: + if ( s === 1 - t ) { - return this.sub( _vector$c ); + const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - } + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; - reflect( normal ) { + } - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length + } - return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + dst[ dstOffset ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; } - angleTo( v ) { - - const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); + static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - if ( denominator === 0 ) return Math.PI / 2; + const x0 = src0[ srcOffset0 ]; + const y0 = src0[ srcOffset0 + 1 ]; + const z0 = src0[ srcOffset0 + 2 ]; + const w0 = src0[ srcOffset0 + 3 ]; - const theta = this.dot( v ) / denominator; + const x1 = src1[ srcOffset1 ]; + const y1 = src1[ srcOffset1 + 1 ]; + const z1 = src1[ srcOffset1 + 2 ]; + const w1 = src1[ srcOffset1 + 3 ]; - // clamp, to handle numerical problems + dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; + dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; + dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; + dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - return Math.acos( clamp( theta, - 1, 1 ) ); + return dst; } - distanceTo( v ) { + get x() { - return Math.sqrt( this.distanceToSquared( v ) ); + return this._x; } - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + set x( value ) { - return dx * dx + dy * dy + dz * dz; + this._x = value; + this._onChangeCallback(); } - manhattanDistanceTo( v ) { + get y() { - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); + return this._y; } - setFromSpherical( s ) { + set y( value ) { - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); + this._y = value; + this._onChangeCallback(); } - setFromSphericalCoords( radius, phi, theta ) { - - const sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); + get z() { - return this; + return this._z; } - setFromCylindrical( c ) { + set z( value ) { - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); + this._z = value; + this._onChangeCallback(); } - setFromCylindricalCoords( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); + get w() { - return this; + return this._w; } - setFromMatrixPosition( m ) { - - const e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; + set w( value ) { - return this; + this._w = value; + this._onChangeCallback(); } - setFromMatrixScale( m ) { + set( x, y, z, w ) { - const sx = this.setFromMatrixColumn( m, 0 ).length(); - const sy = this.setFromMatrixColumn( m, 1 ).length(); - const sz = this.setFromMatrixColumn( m, 2 ).length(); + this._x = x; + this._y = y; + this._z = z; + this._w = w; - this.x = sx; - this.y = sy; - this.z = sz; + this._onChangeCallback(); return this; } - setFromMatrixColumn( m, index ) { - - return this.fromArray( m.elements, index * 4 ); - - } - - setFromMatrix3Column( m, index ) { + clone() { - return this.fromArray( m.elements, index * 3 ); + return new this.constructor( this._x, this._y, this._z, this._w ); } - equals( v ) { - - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - - } + copy( quaternion ) { - fromArray( array, offset = 0 ) { + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; + this._onChangeCallback(); return this; } - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; + setFromEuler( euler, update ) { - return array; + if ( ! ( euler && euler.isEuler ) ) { - } + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - fromBufferAttribute( attribute, index, offset ) { + } - if ( offset !== undefined ) { + const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m - } + const cos = Math.cos; + const sin = Math.sin; - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); + const c1 = cos( x / 2 ); + const c2 = cos( y / 2 ); + const c3 = cos( z / 2 ); - return this; + const s1 = sin( x / 2 ); + const s2 = sin( y / 2 ); + const s3 = sin( z / 2 ); - } + switch ( order ) { - random() { + case '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; + break; - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); + case '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; + break; - return this; + case '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; + break; - } + case '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; + break; - } + case '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; + break; - Vector3.prototype.isVector3 = true; + case '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; + break; - const _vector$c = /*@__PURE__*/ new Vector3(); - const _quaternion$4 = /*@__PURE__*/ new Quaternion(); + default: + console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - class Box3 { + } - constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { + if ( update !== false ) this._onChangeCallback(); - this.min = min; - this.max = max; + return this; } - set( min, max ) { + setFromAxisAngle( axis, angle ) { - this.min.copy( min ); - this.max.copy( max ); + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + const 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; } - setFromArray( array ) { + setFromRotationMatrix( m ) { - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - for ( let i = 0, l = array.length; i < l; i += 3 ) { + const te = m.elements, - const x = array[ i ]; - const y = array[ i + 1 ]; - const z = array[ i + 2 ]; + 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 ], - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + trace = m11 + m22 + m33; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + if ( trace > 0 ) { - } + const s = 0.5 / Math.sqrt( trace + 1.0 ); - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; - return this; + } else if ( m11 > m22 && m11 > m33 ) { - } + const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - setFromBufferAttribute( attribute ) { + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; + } else if ( m22 > m33 ) { - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; + const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - for ( let i = 0, l = attribute.count; i < l; i ++ ) { + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; - const x = attribute.getX( i ); - const y = attribute.getY( i ); - const z = attribute.getZ( i ); + } else { - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; } - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + this._onChangeCallback(); return this; } - setFromPoints( points ) { + setFromUnitVectors( vFrom, vTo ) { - this.makeEmpty(); + // assumes direction vectors vFrom and vTo are normalized - for ( let i = 0, il = points.length; i < il; i ++ ) { + let r = vFrom.dot( vTo ) + 1; - this.expandByPoint( points[ i ] ); + if ( r < Number.EPSILON ) { - } + // vFrom and vTo point in opposite directions - return this; + r = 0; - } + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - setFromCenterAndSize( center, size ) { + this._x = - vFrom.y; + this._y = vFrom.x; + this._z = 0; + this._w = r; - const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); + } else { - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); + this._x = 0; + this._y = - vFrom.z; + this._z = vFrom.y; + this._w = r; - return this; + } - } + } else { + + // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - setFromObject( object ) { + this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; + this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; + this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; + this._w = r; - this.makeEmpty(); + } - return this.expandByObject( object ); + return this.normalize(); } - clone() { + angleTo( q ) { - return new this.constructor().copy( this ); + return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); } - copy( box ) { + rotateTowards( q, step ) { - this.min.copy( box.min ); - this.max.copy( box.max ); + const angle = this.angleTo( q ); + + if ( angle === 0 ) return this; + + const t = Math.min( 1, step / angle ); + + this.slerp( q, t ); return this; } - makeEmpty() { - - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; + identity() { - return this; + return this.set( 0, 0, 0, 1 ); } - isEmpty() { + invert() { - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + // quaternion is assumed to have unit length - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + return this.conjugate(); } - getCenter( target ) { - - if ( target === undefined ) { + conjugate() { - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; - } + this._onChangeCallback(); - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + return this; } - getSize( target ) { + dot( v ) { - if ( target === undefined ) { + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); + } - } + lengthSq() { - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } - expandByPoint( point ) { - - this.min.min( point ); - this.max.max( point ); + length() { - return this; + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); } - expandByVector( vector ) { + normalize() { - this.min.sub( vector ); - this.max.add( vector ); + let l = this.length(); - return this; + if ( l === 0 ) { - } + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; - expandByScalar( scalar ) { + } else { - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); + 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; } - expandByObject( object ) { - - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms + multiply( q, p ) { - object.updateWorldMatrix( false, false ); + if ( p !== undefined ) { - const geometry = object.geometry; + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); - if ( geometry !== undefined ) { + } - if ( geometry.boundingBox === null ) { + return this.multiplyQuaternions( this, q ); - geometry.computeBoundingBox(); + } - } + premultiply( q ) { - _box$3.copy( geometry.boundingBox ); - _box$3.applyMatrix4( object.matrixWorld ); + return this.multiplyQuaternions( q, this ); - this.union( _box$3 ); + } - } + multiplyQuaternions( a, b ) { - const children = object.children; + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - for ( let i = 0, l = children.length; i < l; i ++ ) { + const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - this.expandByObject( children[ i ] ); + 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; } - containsPoint( point ) { + slerp( qb, t ) { - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); - } + const x = this._x, y = this._y, z = this._z, w = this._w; - containsBox( box ) { + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; + let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - } + if ( cosHalfTheta < 0 ) { - getParameter( point, target ) { + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; - // This can potentially have a divide by zero if the box - // has a size dimension of 0. + cosHalfTheta = - cosHalfTheta; - if ( target === undefined ) { + } else { - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); + this.copy( qb ); } - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); - - } + if ( cosHalfTheta >= 1.0 ) { - intersectsBox( box ) { + this._w = w; + this._x = x; + this._y = y; + this._z = z; - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; + return this; - } + } - intersectsSphere( sphere ) { + const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$b ); + if ( sqrSinHalfTheta <= Number.EPSILON ) { - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + const s = 1 - t; + this._w = s * w + t * this._w; + this._x = s * x + t * this._x; + this._y = s * y + t * this._y; + this._z = s * z + t * this._z; - } + this.normalize(); + this._onChangeCallback(); - intersectsPlane( plane ) { + return this; - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. + } - let min, max; + const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); + const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); + const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - if ( plane.normal.x > 0 ) { + 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 ); - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; + this._onChangeCallback(); - } else { + return this; - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; + } - } + slerpQuaternions( qa, qb, t ) { - if ( plane.normal.y > 0 ) { + return this.copy( qa ).slerp( qb, t ); - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; + } - } else { + random() { - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; + // Derived from http://planning.cs.uiuc.edu/node198.html + // Note, this source uses w, x, y, z ordering, + // so we swap the order below. - } + const u1 = Math.random(); + const sqrt1u1 = Math.sqrt( 1 - u1 ); + const sqrtu1 = Math.sqrt( u1 ); - if ( plane.normal.z > 0 ) { + const u2 = 2 * Math.PI * Math.random(); - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; + const u3 = 2 * Math.PI * Math.random(); - } else { + return this.set( + sqrt1u1 * Math.cos( u2 ), + sqrtu1 * Math.sin( u3 ), + sqrtu1 * Math.cos( u3 ), + sqrt1u1 * Math.sin( u2 ), + ); - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; + } - } + equals( quaternion ) { - return ( min <= - plane.constant && max >= - plane.constant ); + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } - intersectsTriangle( triangle ) { + fromArray( array, offset = 0 ) { - if ( this.isEmpty() ) { + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; - return false; + this._onChangeCallback(); - } + return this; - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); + } - // translate triangle to aabb origin - _v0$2.subVectors( triangle.a, _center ); - _v1$7.subVectors( triangle.b, _center ); - _v2$3.subVectors( triangle.c, _center ); + toArray( array = [], offset = 0 ) { - // compute edge vectors for triangle - _f0.subVectors( _v1$7, _v0$2 ); - _f1.subVectors( _v2$3, _v1$7 ); - _f2.subVectors( _v0$2, _v2$3 ); + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - let axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { + return array; - return false; + } - } + fromBufferAttribute( attribute, index ) { - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { + this._x = attribute.getX( index ); + this._y = attribute.getY( index ); + this._z = attribute.getZ( index ); + this._w = attribute.getW( index ); - return false; + return this; - } + } - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; + _onChange( callback ) { - return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); + this._onChangeCallback = callback; + + return this; } - clampPoint( point, target ) { + _onChangeCallback() {} - if ( target === undefined ) { + } - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); + Quaternion.prototype.isQuaternion = true; - } + class Vector3 { - return target.copy( point ).clamp( this.min, this.max ); + constructor( x = 0, y = 0, z = 0 ) { + + this.x = x; + this.y = y; + this.z = z; } - distanceToPoint( point ) { + set( x, y, z ) { - const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); + if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) - return clampedPoint.sub( point ).length(); + this.x = x; + this.y = y; + this.z = z; + + return this; } - getBoundingSphere( target ) { + setScalar( scalar ) { - if ( target === undefined ) { + this.x = scalar; + this.y = scalar; + this.z = scalar; - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency + return this; - } + } - this.getCenter( target.center ); + setX( x ) { - target.radius = this.getSize( _vector$b ).length() * 0.5; + this.x = x; - return target; + return this; } - intersect( box ) { - - this.min.max( box.min ); - this.max.min( box.max ); + setY( y ) { - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); + this.y = y; return this; } - union( box ) { + setZ( z ) { - this.min.min( box.min ); - this.max.max( box.max ); + this.z = z; return this; } - applyMatrix4( matrix ) { + setComponent( index, value ) { - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; + switch ( index ) { - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + 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 ); - this.setFromPoints( _points ); + } return this; } - translate( offset ) { + getComponent( index ) { - this.min.add( offset ); - this.max.add( offset ); + switch ( index ) { - return this; + 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 ); + + } } - equals( box ) { + clone() { - return box.min.equals( this.min ) && box.max.equals( this.max ); + return new this.constructor( this.x, this.y, this.z ); } - } - - Box3.prototype.isBox3 = true; + copy( v ) { - const _points = [ - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3() - ]; + this.x = v.x; + this.y = v.y; + this.z = v.z; - const _vector$b = /*@__PURE__*/ new Vector3(); + return this; - const _box$3 = /*@__PURE__*/ new Box3(); + } - // triangle centered vertices + add( v, w ) { - const _v0$2 = /*@__PURE__*/ new Vector3(); - const _v1$7 = /*@__PURE__*/ new Vector3(); - const _v2$3 = /*@__PURE__*/ new Vector3(); + if ( w !== undefined ) { - // triangle edge vectors + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); - const _f0 = /*@__PURE__*/ new Vector3(); - const _f1 = /*@__PURE__*/ new Vector3(); - const _f2 = /*@__PURE__*/ new Vector3(); + } - const _center = /*@__PURE__*/ new Vector3(); - const _extents = /*@__PURE__*/ new Vector3(); - const _triangleNormal = /*@__PURE__*/ new Vector3(); - const _testAxis = /*@__PURE__*/ new Vector3(); + this.x += v.x; + this.y += v.y; + this.z += v.z; - function satForAxes( axes, v0, v1, v2, extents ) { + return this; - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { + } - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { + addScalar( s ) { - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; + this.x += s; + this.y += s; + this.z += s; - } + return this; } - return true; - - } - - const _box$2 = /*@__PURE__*/ new Box3(); - const _v1$6 = /*@__PURE__*/ new Vector3(); - const _toFarthestPoint = /*@__PURE__*/ new Vector3(); - const _toPoint = /*@__PURE__*/ new Vector3(); - - class Sphere { + addVectors( a, b ) { - constructor( center = new Vector3(), radius = - 1 ) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; - this.center = center; - this.radius = radius; + return this; } - set( center, radius ) { + addScaledVector( v, s ) { - this.center.copy( center ); - this.radius = radius; + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; return this; } - setFromPoints( points, optionalCenter ) { - - const center = this.center; - - if ( optionalCenter !== undefined ) { - - center.copy( optionalCenter ); + sub( v, w ) { - } else { + if ( w !== undefined ) { - _box$2.setFromPoints( points ).getCenter( center ); + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); } - let maxRadiusSq = 0; + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; - for ( let i = 0, il = points.length; i < il; i ++ ) { + return this; - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + } - } + subScalar( s ) { - this.radius = Math.sqrt( maxRadiusSq ); + this.x -= s; + this.y -= s; + this.z -= s; return this; } - copy( sphere ) { + subVectors( a, b ) { - this.center.copy( sphere.center ); - this.radius = sphere.radius; + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; return this; } - isEmpty() { + multiply( v, w ) { - return ( this.radius < 0 ); + if ( w !== undefined ) { - } + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); - makeEmpty() { + } - this.center.set( 0, 0, 0 ); - this.radius = - 1; + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; return this; } - containsPoint( point ) { - - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - - } + multiplyScalar( scalar ) { - distanceToPoint( point ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; - return ( point.distanceTo( this.center ) - this.radius ); + return this; } - intersectsSphere( sphere ) { + multiplyVectors( a, b ) { - const radiusSum = this.radius + sphere.radius; + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + return this; } - intersectsBox( box ) { + applyEuler( euler ) { - return box.intersectsSphere( this ); + if ( ! ( euler && euler.isEuler ) ) { - } + console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - intersectsPlane( plane ) { + } - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; + return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } - clampPoint( point, target ) { + applyAxisAngle( axis, angle ) { - const deltaLengthSq = this.center.distanceToSquared( point ); + return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); - if ( target === undefined ) { + } - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); + applyMatrix3( m ) { - } + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - target.copy( point ); + 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; - if ( deltaLengthSq > ( this.radius * this.radius ) ) { + return this; - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); + } - } + applyNormalMatrix( m ) { - return target; + return this.applyMatrix3( m ).normalize(); } - getBoundingBox( target ) { + applyMatrix4( m ) { - if ( target === undefined ) { + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); + const 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; - if ( this.isEmpty() ) { + return this; - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; + } - } + applyQuaternion( q ) { - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); + const x = this.x, y = this.y, z = this.z; + const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - return target; + // calculate quat * vector - } + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = - qx * x - qy * y - qz * z; - applyMatrix4( matrix ) { + // calculate result * inverse quat - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); + 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; } - translate( offset ) { - - this.center.add( offset ); + project( camera ) { - return this; + return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } - expandByPoint( point ) { + unproject( camera ) { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 + return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - _toPoint.subVectors( point, this.center ); + } - const lengthSq = _toPoint.lengthSq(); + transformDirection( m ) { - if ( lengthSq > ( this.radius * this.radius ) ) { + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction - const length = Math.sqrt( lengthSq ); - const missingRadiusHalf = ( length - this.radius ) * 0.5; + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - // Nudge this sphere towards the target point. Add half the missing distance to radius, - // and the other half to position. This gives a tighter enclosure, instead of if - // the whole missing distance were just added to radius. + 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; - this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); - this.radius += missingRadiusHalf; + return this.normalize(); - } + } + + divide( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; return this; } - union( sphere ) { + divideScalar( scalar ) { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 + return this.multiplyScalar( 1 / scalar ); - // To enclose another sphere into this sphere, we only need to enclose two points: - // 1) Enclose the farthest point on the other sphere into this sphere. - // 2) Enclose the opposite point of the farthest point into this sphere. + } - _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); + min( v ) { - this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); - this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); + 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; } - equals( sphere ) { + max( v ) { - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + 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; - clone() { + } - return new this.constructor().copy( this ); + clamp( 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 ) ); - const _vector$a = /*@__PURE__*/ new Vector3(); - const _segCenter = /*@__PURE__*/ new Vector3(); - const _segDir = /*@__PURE__*/ new Vector3(); - const _diff = /*@__PURE__*/ new Vector3(); + return this; - const _edge1 = /*@__PURE__*/ new Vector3(); - const _edge2 = /*@__PURE__*/ new Vector3(); - const _normal$1 = /*@__PURE__*/ new Vector3(); + } - class Ray { + clampScalar( minVal, maxVal ) { - constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.origin = origin; - this.direction = direction; + return this; } - set( origin, direction ) { + clampLength( min, max ) { - this.origin.copy( origin ); - this.direction.copy( direction ); + const length = this.length(); - return this; + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } - copy( ray ) { + floor() { - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); return this; } - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); + ceil() { - } + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + return this; } - lookAt( v ) { + round() { - this.direction.copy( v ).sub( this.origin ).normalize(); + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); return this; } - recast( t ) { + roundToZero() { - this.origin.copy( this.at( t, _vector$a ) ); + 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; } - closestPointToPoint( point, target ) { + negate() { - if ( target === undefined ) { + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); + return this; - } + } - target.subVectors( point, this.origin ); + dot( v ) { - const directionDistance = target.dot( this.direction ); + return this.x * v.x + this.y * v.y + this.z * v.z; - if ( directionDistance < 0 ) { + } - return target.copy( this.origin ); + // TODO lengthSquared? - } + lengthSq() { - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + return this.x * this.x + this.y * this.y + this.z * this.z; } - distanceToPoint( point ) { + length() { - return Math.sqrt( this.distanceSqToPoint( point ) ); + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } - distanceSqToPoint( point ) { + manhattanLength() { - const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - // point behind the ray + } - if ( directionDistance < 0 ) { + normalize() { - return this.origin.distanceToSquared( point ); + return this.divideScalar( this.length() || 1 ); - } + } - _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + setLength( length ) { - return _vector$a.distanceToSquared( point ); + return this.normalize().multiplyScalar( length ); } - distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + lerp( v, alpha ) { - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); + return this; - const segExtent = v0.distanceTo( v1 ) * 0.5; - const a01 = - this.direction.dot( _segDir ); - const b0 = _diff.dot( this.direction ); - const b1 = - _diff.dot( _segDir ); - const c = _diff.lengthSq(); - const det = Math.abs( 1 - a01 * a01 ); - let s0, s1, sqrDist, extDet; + } - if ( det > 0 ) { + lerpVectors( v1, v2, alpha ) { - // The ray and segment are not parallel. + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; + return this; - if ( s0 >= 0 ) { + } - if ( s1 >= - extDet ) { + cross( v, w ) { - if ( s1 <= extDet ) { + if ( w !== undefined ) { - // region 0 - // Minimum at interior points of ray and segment. + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); - const invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + } - } else { + return this.crossVectors( this, v ); - // region 1 + } - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + crossVectors( a, b ) { - } + const ax = a.x, ay = a.y, az = a.z; + const bx = b.x, by = b.y, bz = b.z; - } else { + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; - // region 5 + return this; - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + } - } + projectOnVector( v ) { - } else { + const denominator = v.lengthSq(); - if ( s1 <= - extDet ) { + if ( denominator === 0 ) return this.set( 0, 0, 0 ); - // region 4 + const scalar = v.dot( this ) / denominator; - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + return this.copy( v ).multiplyScalar( scalar ); - } else if ( s1 <= extDet ) { + } - // region 3 + projectOnPlane( planeNormal ) { - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; + _vector$c.copy( this ).projectOnVector( planeNormal ); - } else { + return this.sub( _vector$c ); - // region 2 + } - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - - } + reflect( normal ) { - } + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length - } else { + return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - // Ray and segment are parallel. + } - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + angleTo( v ) { - } + const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - if ( optionalPointOnRay ) { + if ( denominator === 0 ) return Math.PI / 2; - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + const theta = this.dot( v ) / denominator; - } + // clamp, to handle numerical problems - if ( optionalPointOnSegment ) { + return Math.acos( clamp( theta, - 1, 1 ) ); - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); + } - } + distanceTo( v ) { - return sqrDist; + return Math.sqrt( this.distanceToSquared( v ) ); } - intersectSphere( sphere, target ) { + distanceToSquared( v ) { - _vector$a.subVectors( sphere.center, this.origin ); - const tca = _vector$a.dot( this.direction ); - const d2 = _vector$a.dot( _vector$a ) - tca * tca; - const radius2 = sphere.radius * sphere.radius; + const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - if ( d2 > radius2 ) return null; + return dx * dx + dy * dy + dz * dz; - const thc = Math.sqrt( radius2 - d2 ); + } - // t0 = first intersect point - entrance on front of sphere - const t0 = tca - thc; + manhattanDistanceTo( v ) { - // t1 = second intersect point - exit point on back of sphere - const t1 = tca + thc; + return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; + } - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); + setFromSpherical( s ) { - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); + return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } - intersectsSphere( sphere ) { + setFromSphericalCoords( radius, phi, theta ) { - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); + const sinPhiRadius = Math.sin( phi ) * radius; - } + this.x = sinPhiRadius * Math.sin( theta ); + this.y = Math.cos( phi ) * radius; + this.z = sinPhiRadius * Math.cos( theta ); - distanceToPlane( plane ) { + return this; - const denominator = plane.normal.dot( this.direction ); + } - if ( denominator === 0 ) { + setFromCylindrical( c ) { - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { + return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - return 0; + } - } + setFromCylindricalCoords( radius, theta, y ) { - // Null is preferable to undefined since undefined means.... it is undefined + this.x = radius * Math.sin( theta ); + this.y = y; + this.z = radius * Math.cos( theta ); - return null; + return this; - } + } - const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + setFromMatrixPosition( m ) { - // Return if the ray never intersects the plane + const e = m.elements; - return t >= 0 ? t : null; + this.x = e[ 12 ]; + this.y = e[ 13 ]; + this.z = e[ 14 ]; + + return this; } - intersectPlane( plane, target ) { + setFromMatrixScale( m ) { - const t = this.distanceToPlane( plane ); + const sx = this.setFromMatrixColumn( m, 0 ).length(); + const sy = this.setFromMatrixColumn( m, 1 ).length(); + const sz = this.setFromMatrixColumn( m, 2 ).length(); - if ( t === null ) { + this.x = sx; + this.y = sy; + this.z = sz; - return null; + return this; - } + } - return this.at( t, target ); + setFromMatrixColumn( m, index ) { + + return this.fromArray( m.elements, index * 4 ); } - intersectsPlane( plane ) { + setFromMatrix3Column( m, index ) { - // check if the ray lies on the plane first + return this.fromArray( m.elements, index * 3 ); - const distToPoint = plane.distanceToPoint( this.origin ); + } - if ( distToPoint === 0 ) { + setFromEuler( e ) { - return true; + this.x = e._x; + this.y = e._y; + this.z = e._z; - } + return this; - const denominator = plane.normal.dot( this.direction ); + } - if ( denominator * distToPoint < 0 ) { + equals( v ) { - return true; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - } + } - // ray origin is behind the plane (and is pointing behind it) + fromArray( array, offset = 0 ) { - return false; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; - } + return this; - intersectBox( box, target ) { + } - let tmin, tmax, tymin, tymax, tzmin, tzmax; + toArray( array = [], offset = 0 ) { - const invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; - const origin = this.origin; + return array; - if ( invdirx >= 0 ) { + } - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; + fromBufferAttribute( attribute, index, offset ) { - } else { + if ( offset !== undefined ) { - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; + console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); } - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); - } else { + return this; - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; + } - } + random() { - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN + return this; - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + } - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + randomDirection() { - if ( invdirz >= 0 ) { + // Derived from https://mathworld.wolfram.com/SpherePointPicking.html - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; + const u = ( Math.random() - 0.5 ) * 2; + const t = Math.random() * Math.PI * 2; + const f = Math.sqrt( 1 - u ** 2 ); - } else { + this.x = f * Math.cos( t ); + this.y = f * Math.sin( t ); + this.z = u; - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; + return this; - } + } - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + *[ Symbol.iterator ]() { - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + yield this.x; + yield this.y; + yield this.z; - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + } - //return point closest to the ray (positive side) + } - if ( tmax < 0 ) return null; + Vector3.prototype.isVector3 = true; - return this.at( tmin >= 0 ? tmin : tmax, target ); + const _vector$c = /*@__PURE__*/ new Vector3(); + const _quaternion$4 = /*@__PURE__*/ new Quaternion(); - } + class Box3 { - intersectsBox( box ) { + constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { - return this.intersectBox( box, _vector$a ) !== null; + this.min = min; + this.max = max; } - intersectTriangle( a, b, c, backfaceCulling, target ) { + set( min, max ) { - // Compute the offset origin, edges, and normal. + this.min.copy( min ); + this.max.copy( max ); - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + return this; - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal$1.crossVectors( _edge1, _edge2 ); + } - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal$1 ); - let sign; + setFromArray( array ) { - if ( DdN > 0 ) { + let minX = + Infinity; + let minY = + Infinity; + let minZ = + Infinity; - if ( backfaceCulling ) return null; - sign = 1; + let maxX = - Infinity; + let maxY = - Infinity; + let maxZ = - Infinity; - } else if ( DdN < 0 ) { + for ( let i = 0, l = array.length; i < l; i += 3 ) { - sign = - 1; - DdN = - DdN; + const x = array[ i ]; + const y = array[ i + 1 ]; + const z = array[ i + 2 ]; - } else { + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - return null; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; } - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + return this; - return null; + } - } + setFromBufferAttribute( attribute ) { - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); + let minX = + Infinity; + let minY = + Infinity; + let minZ = + Infinity; - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + let maxX = - Infinity; + let maxY = - Infinity; + let maxZ = - Infinity; - return null; + for ( let i = 0, l = attribute.count; i < l; i ++ ) { - } + const x = attribute.getX( i ); + const y = attribute.getY( i ); + const z = attribute.getZ( i ); - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - return null; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; } - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal$1 ); + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); - // t < 0, no intersection - if ( QdN < 0 ) { + return this; - return null; + } + + setFromPoints( points ) { + + this.makeEmpty(); + + for ( let i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ); } - // Ray intersects triangle. - return this.at( QdN / DdN, target ); + return this; } - applyMatrix4( matrix4 ) { + setFromCenterAndSize( center, size ) { - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); + const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); return this; } - equals( ray ) { + setFromObject( object, precise = false ) { - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + this.makeEmpty(); + + return this.expandByObject( object, precise ); } @@ -6647,4138 +6564,4398 @@ } - } + copy( box ) { - class Matrix4 { + this.min.copy( box.min ); + this.max.copy( box.max ); - constructor() { + return this; - this.elements = [ + } - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + makeEmpty() { - ]; + this.min.x = this.min.y = this.min.z = + Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; - if ( arguments.length > 0 ) { + return this; - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + } - } + isEmpty() { - } + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - const 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; + getCenter( target ) { - return this; + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } - identity() { + getSize( target ) { - this.set( + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ); + expandByPoint( point ) { + + this.min.min( point ); + this.max.max( point ); return this; } - clone() { + expandByVector( vector ) { - return new Matrix4().fromArray( this.elements ); + this.min.sub( vector ); + this.max.add( vector ); - } + return this; - copy( m ) { + } - const te = this.elements; - const me = m.elements; + expandByScalar( scalar ) { - 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 ]; + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); return this; } - copyPosition( m ) { + expandByObject( object, precise = false ) { - const te = this.elements, me = m.elements; + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; + object.updateWorldMatrix( false, false ); - return this; + const geometry = object.geometry; - } + if ( geometry !== undefined ) { - setFromMatrix3( m ) { + if ( precise && geometry.attributes != undefined && geometry.attributes.position !== undefined ) { - const me = m.elements; + const position = geometry.attributes.position; + for ( let i = 0, l = position.count; i < l; i ++ ) { - this.set( + _vector$b.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); + this.expandByPoint( _vector$b ); - me[ 0 ], me[ 3 ], me[ 6 ], 0, - me[ 1 ], me[ 4 ], me[ 7 ], 0, - me[ 2 ], me[ 5 ], me[ 8 ], 0, - 0, 0, 0, 1 + } - ); + } else { - return this; + if ( geometry.boundingBox === null ) { - } + geometry.computeBoundingBox(); - extractBasis( xAxis, yAxis, zAxis ) { + } - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); + _box$3.copy( geometry.boundingBox ); + _box$3.applyMatrix4( object.matrixWorld ); - return this; + this.union( _box$3 ); - } + } - makeBasis( 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 - ); + const children = object.children; + + for ( let i = 0, l = children.length; i < l; i ++ ) { + + this.expandByObject( children[ i ], precise ); + + } return this; } - extractRotation( m ) { + containsPoint( point ) { - // this method does not support reflection matrices + return point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ? false : true; - const te = this.elements; - const me = m.elements; + } - const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); + containsBox( box ) { - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; + return this.min.x <= box.min.x && box.max.x <= this.max.x && + this.min.y <= box.min.y && box.max.y <= this.max.y && + this.min.z <= box.min.z && box.max.z <= this.max.z; - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; + } - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; + getParameter( point, target ) { - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + // This can potentially have a divide by zero if the box + // has a size dimension of 0. - return this; + return target.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); } - makeRotationFromEuler( euler ) { + intersectsBox( box ) { - if ( ! ( euler && euler.isEuler ) ) { + // using 6 splitting planes to rule out intersections. + return box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } - } + intersectsSphere( sphere ) { - const te = this.elements; + // Find the point on the AABB closest to the sphere center. + this.clampPoint( sphere.center, _vector$b ); - const x = euler.x, y = euler.y, z = euler.z; - const a = Math.cos( x ), b = Math.sin( x ); - const c = Math.cos( y ), d = Math.sin( y ); - const e = Math.cos( z ), f = Math.sin( z ); + // If that point is inside the sphere, the AABB and sphere intersect. + return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - if ( euler.order === 'XYZ' ) { + } - const ae = a * e, af = a * f, be = b * e, bf = b * f; + intersectsPlane( plane ) { - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; + let min, max; - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; + if ( plane.normal.x > 0 ) { - } else if ( euler.order === 'YXZ' ) { + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; - const ce = c * e, cf = c * f, de = d * e, df = d * f; + } else { - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; - 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; + if ( plane.normal.y > 0 ) { - } else if ( euler.order === 'ZXY' ) { + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; - const ce = c * e, cf = c * f, de = d * e, df = d * f; + } else { - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; - 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; + if ( plane.normal.z > 0 ) { - } else if ( euler.order === 'ZYX' ) { + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; - const ae = a * e, af = a * f, be = b * e, bf = b * f; + } else { - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; - 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; + return ( min <= - plane.constant && max >= - plane.constant ); - } else if ( euler.order === 'YZX' ) { + } - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; + intersectsTriangle( triangle ) { - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; + if ( this.isEmpty() ) { - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; + return false; - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; + } - } else if ( euler.order === 'XZY' ) { + // compute box center and extents + this.getCenter( _center ); + _extents.subVectors( this.max, _center ); - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; + // translate triangle to aabb origin + _v0$2.subVectors( triangle.a, _center ); + _v1$7.subVectors( triangle.b, _center ); + _v2$3.subVectors( triangle.c, _center ); - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; + // compute edge vectors for triangle + _f0.subVectors( _v1$7, _v0$2 ); + _f1.subVectors( _v2$3, _v1$7 ); + _f2.subVectors( _v0$2, _v2$3 ); - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; + // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb + // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation + // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) + let axes = [ + 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, + _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, + - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 + ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; + return false; } - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + // test 3 face normals from the aabb + axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - return this; + return false; - } + } - makeRotationFromQuaternion( q ) { + // finally testing the face normal of the triangle + // use already existing triangle edge vectors here + _triangleNormal.crossVectors( _f0, _f1 ); + axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - return this.compose( _zero, q, _one ); + return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); } - lookAt( eye, target, up ) { + clampPoint( point, target ) { - const te = this.elements; + return target.copy( point ).clamp( this.min, this.max ); - _z.subVectors( eye, target ); + } - if ( _z.lengthSq() === 0 ) { + distanceToPoint( point ) { - // eye and target are in the same position + const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); - _z.z = 1; + return clampedPoint.sub( point ).length(); - } + } - _z.normalize(); - _x.crossVectors( up, _z ); + getBoundingSphere( target ) { - if ( _x.lengthSq() === 0 ) { + this.getCenter( target.center ); - // up and z are parallel + target.radius = this.getSize( _vector$b ).length() * 0.5; - if ( Math.abs( up.z ) === 1 ) { + return target; - _z.x += 0.0001; + } - } else { + intersect( box ) { - _z.z += 0.0001; + this.min.max( box.min ); + this.max.min( box.max ); - } + // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. + if ( this.isEmpty() ) this.makeEmpty(); - _z.normalize(); - _x.crossVectors( up, _z ); + return this; - } + } - _x.normalize(); - _y.crossVectors( _z, _x ); + union( box ) { - 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; + this.min.min( box.min ); + this.max.max( box.max ); return this; } - multiply( m, n ) { + applyMatrix4( matrix ) { - if ( n !== undefined ) { + // transform of empty box is an empty box. + if ( this.isEmpty() ) return this; - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - } + this.setFromPoints( _points ); - return this.multiplyMatrices( this, m ); + return this; } - premultiply( m ) { + translate( offset ) { - return this.multiplyMatrices( m, this ); + this.min.add( offset ); + this.max.add( offset ); + + return this; } - multiplyMatrices( a, b ) { + equals( box ) { - const ae = a.elements; - const be = b.elements; - const te = this.elements; + return box.min.equals( this.min ) && box.max.equals( this.max ); - const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + } - const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - const 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; + Box3.prototype.isBox3 = true; - 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; + const _points = [ + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3() + ]; - 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; + const _vector$b = /*@__PURE__*/ new Vector3(); - 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; + const _box$3 = /*@__PURE__*/ new Box3(); - return this; + // triangle centered vertices - } + const _v0$2 = /*@__PURE__*/ new Vector3(); + const _v1$7 = /*@__PURE__*/ new Vector3(); + const _v2$3 = /*@__PURE__*/ new Vector3(); - multiplyScalar( s ) { + // triangle edge vectors - const te = this.elements; + const _f0 = /*@__PURE__*/ new Vector3(); + const _f1 = /*@__PURE__*/ new Vector3(); + const _f2 = /*@__PURE__*/ new Vector3(); - 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; + const _center = /*@__PURE__*/ new Vector3(); + const _extents = /*@__PURE__*/ new Vector3(); + const _triangleNormal = /*@__PURE__*/ new Vector3(); + const _testAxis = /*@__PURE__*/ new Vector3(); - return this; + function satForAxes( axes, v0, v1, v2, extents ) { - } + for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - determinant() { + _testAxis.fromArray( axes, i ); + // project the aabb onto the separating axis + const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); + // project all 3 vertices of the triangle onto the separating axis + const p0 = v0.dot( _testAxis ); + const p1 = v1.dot( _testAxis ); + const p2 = v2.dot( _testAxis ); + // actual test, basically see if either of the most extreme of the triangle points intersects r + if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - const te = this.elements; + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is separating and we can exit + return false; - const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - const 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 - ) + return true; - ); + } - } + const _box$2 = /*@__PURE__*/ new Box3(); + const _v1$6 = /*@__PURE__*/ new Vector3(); + const _toFarthestPoint = /*@__PURE__*/ new Vector3(); + const _toPoint = /*@__PURE__*/ new Vector3(); - transpose() { + class Sphere { - const te = this.elements; - let tmp; + constructor( center = new Vector3(), radius = - 1 ) { - 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; + this.center = center; + this.radius = radius; - 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; + } + + set( center, radius ) { + + this.center.copy( center ); + this.radius = radius; return this; } - setPosition( x, y, z ) { + setFromPoints( points, optionalCenter ) { - const te = this.elements; + const center = this.center; - if ( x.isVector3 ) { + if ( optionalCenter !== undefined ) { - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; + center.copy( optionalCenter ); } else { - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; + _box$2.setFromPoints( points ).getCenter( center ); } - return this; + let maxRadiusSq = 0; - } + for ( let i = 0, il = points.length; i < il; i ++ ) { - invert() { + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - const te = this.elements, + } - n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], - n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], - n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], - n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], + this.radius = Math.sqrt( maxRadiusSq ); - 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; + return this; - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + } - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + copy( sphere ) { - const detInv = 1 / det; + this.center.copy( sphere.center ); + this.radius = sphere.radius; - 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; + return this; - 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; + isEmpty() { - 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.radius < 0 ); + + } + + makeEmpty() { + + this.center.set( 0, 0, 0 ); + this.radius = - 1; return this; } - scale( v ) { + containsPoint( point ) { - const te = this.elements; - const x = v.x, y = v.y, z = v.z; + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - 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; + distanceToPoint( point ) { - } + return ( point.distanceTo( this.center ) - this.radius ); - getMaxScaleOnAxis() { + } - const te = this.elements; + intersectsSphere( sphere ) { - const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + const radiusSum = this.radius + sphere.radius; - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } - makeTranslation( x, y, z ) { + intersectsBox( box ) { - this.set( + return box.intersectsSphere( this ); - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 + } - ); + intersectsPlane( plane ) { - return this; + return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } - makeRotationX( theta ) { + clampPoint( point, target ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + const deltaLengthSq = this.center.distanceToSquared( point ); - this.set( + target.copy( point ); - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 + if ( deltaLengthSq > ( this.radius * this.radius ) ) { - ); + target.sub( this.center ).normalize(); + target.multiplyScalar( this.radius ).add( this.center ); - return this; + } + + return target; } - makeRotationY( theta ) { + getBoundingBox( target ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + if ( this.isEmpty() ) { - this.set( + // Empty sphere produces empty bounding box + target.makeEmpty(); + return target; - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 + } - ); + target.set( this.center, this.center ); + target.expandByScalar( this.radius ); - return this; + return target; } - makeRotationZ( theta ) { + applyMatrix4( matrix ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); - this.set( + return this; - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ); + translate( offset ) { + + this.center.add( offset ); return this; } - makeRotationAxis( axis, angle ) { + expandByPoint( point ) { - // Based on http://www.gamedev.net/reference/articles/article1199.asp - - const c = Math.cos( angle ); - const s = Math.sin( angle ); - const t = 1 - c; - const x = axis.x, y = axis.y, z = axis.z; - const 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 + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 - ); + _toPoint.subVectors( point, this.center ); - return this; + const lengthSq = _toPoint.lengthSq(); - } + if ( lengthSq > ( this.radius * this.radius ) ) { - makeScale( x, y, z ) { + const length = Math.sqrt( lengthSq ); + const missingRadiusHalf = ( length - this.radius ) * 0.5; - this.set( + // Nudge this sphere towards the target point. Add half the missing distance to radius, + // and the other half to position. This gives a tighter enclosure, instead of if + // the whole missing distance were just added to radius. - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 + this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); + this.radius += missingRadiusHalf; - ); + } return this; } - makeShear( x, y, z ) { - - this.set( - - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 - - ); - - return this; + union( sphere ) { - } + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 - compose( position, quaternion, scale ) { + // To enclose another sphere into this sphere, we only need to enclose two points: + // 1) Enclose the farthest point on the other sphere into this sphere. + // 2) Enclose the opposite point of the farthest point into this sphere. - const te = this.elements; + if ( this.center.equals( sphere.center ) === true ) { - const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - const x2 = x + x, y2 = y + y, z2 = z + z; - const xx = x * x2, xy = x * y2, xz = x * z2; - const yy = y * y2, yz = y * z2, zz = z * z2; - const wx = w * x2, wy = w * y2, wz = w * z2; + _toFarthestPoint.set( 0, 0, 1 ).multiplyScalar( sphere.radius ); - const sx = scale.x, sy = scale.y, sz = scale.z; - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; + } else { - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; + _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; + } - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; + this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); + this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); return this; } - decompose( position, quaternion, scale ) { - - const te = this.elements; + equals( sphere ) { - let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - // if determine is negative, we need to invert one scale - const det = this.determinant(); - if ( det < 0 ) sx = - sx; + } - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; + clone() { - // scale the rotation part - _m1$2.copy( this ); + return new this.constructor().copy( this ); - const invSX = 1 / sx; - const invSY = 1 / sy; - const invSZ = 1 / sz; + } - _m1$2.elements[ 0 ] *= invSX; - _m1$2.elements[ 1 ] *= invSX; - _m1$2.elements[ 2 ] *= invSX; + } - _m1$2.elements[ 4 ] *= invSY; - _m1$2.elements[ 5 ] *= invSY; - _m1$2.elements[ 6 ] *= invSY; + const _vector$a = /*@__PURE__*/ new Vector3(); + const _segCenter = /*@__PURE__*/ new Vector3(); + const _segDir = /*@__PURE__*/ new Vector3(); + const _diff = /*@__PURE__*/ new Vector3(); - _m1$2.elements[ 8 ] *= invSZ; - _m1$2.elements[ 9 ] *= invSZ; - _m1$2.elements[ 10 ] *= invSZ; + const _edge1 = /*@__PURE__*/ new Vector3(); + const _edge2 = /*@__PURE__*/ new Vector3(); + const _normal$1 = /*@__PURE__*/ new Vector3(); - quaternion.setFromRotationMatrix( _m1$2 ); + class Ray { - scale.x = sx; - scale.y = sy; - scale.z = sz; + constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { - return this; + this.origin = origin; + this.direction = direction; } - makePerspective( left, right, top, bottom, near, far ) { - - if ( far === undefined ) { - - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - - } - - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); - - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - ( far + near ) / ( far - near ); - const d = - 2 * far * near / ( far - near ); + set( origin, direction ) { - 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; + this.origin.copy( origin ); + this.direction.copy( direction ); return this; } - makeOrthographic( left, right, top, bottom, near, far ) { - - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); - - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = ( far + near ) * p; + copy( ray ) { - 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; + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); return this; } - equals( matrix ) { + at( t, target ) { - const te = this.elements; - const me = matrix.elements; + return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - for ( let i = 0; i < 16; i ++ ) { + } - if ( te[ i ] !== me[ i ] ) return false; + lookAt( v ) { - } + this.direction.copy( v ).sub( this.origin ).normalize(); - return true; + return this; } - fromArray( array, offset = 0 ) { - - for ( let i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; + recast( t ) { - } + this.origin.copy( this.at( t, _vector$a ) ); return this; } - toArray( array = [], offset = 0 ) { + closestPointToPoint( point, target ) { - const te = this.elements; + target.subVectors( point, this.origin ); - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; + const directionDistance = target.dot( this.direction ); - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; + if ( directionDistance < 0 ) { - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; + return target.copy( this.origin ); - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; + } - return array; + return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); } - } - - Matrix4.prototype.isMatrix4 = true; - - const _v1$5 = /*@__PURE__*/ new Vector3(); - const _m1$2 = /*@__PURE__*/ new Matrix4(); - const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); - const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); - const _x = /*@__PURE__*/ new Vector3(); - const _y = /*@__PURE__*/ new Vector3(); - const _z = /*@__PURE__*/ new Vector3(); + distanceToPoint( point ) { - const _matrix$1 = /*@__PURE__*/ new Matrix4(); - const _quaternion$3 = /*@__PURE__*/ new Quaternion(); + return Math.sqrt( this.distanceSqToPoint( point ) ); - class Euler { + } - constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { + distanceSqToPoint( point ) { - this._x = x; - this._y = y; - this._z = z; - this._order = order; + const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); - } + // point behind the ray - get x() { + if ( directionDistance < 0 ) { - return this._x; + return this.origin.distanceToSquared( point ); - } + } - set x( value ) { + _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - this._x = value; - this._onChangeCallback(); + return _vector$a.distanceToSquared( point ); } - get y() { + distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - return this._y; + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment - } + _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + _segDir.copy( v1 ).sub( v0 ).normalize(); + _diff.copy( this.origin ).sub( _segCenter ); - set y( value ) { + const segExtent = v0.distanceTo( v1 ) * 0.5; + const a01 = - this.direction.dot( _segDir ); + const b0 = _diff.dot( this.direction ); + const b1 = - _diff.dot( _segDir ); + const c = _diff.lengthSq(); + const det = Math.abs( 1 - a01 * a01 ); + let s0, s1, sqrDist, extDet; - this._y = value; - this._onChangeCallback(); + if ( det > 0 ) { - } + // The ray and segment are not parallel. - get z() { + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; - return this._z; + if ( s0 >= 0 ) { - } + if ( s1 >= - extDet ) { - set z( value ) { + if ( s1 <= extDet ) { - this._z = value; - this._onChangeCallback(); + // region 0 + // Minimum at interior points of ray and segment. - } + const invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - get order() { + } else { - return this._order; + // region 1 - } + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - set order( value ) { + } - this._order = value; - this._onChangeCallback(); + } else { - } + // region 5 - set( x, y, z, order ) { + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; + } - this._onChangeCallback(); + } else { - return this; + if ( s1 <= - extDet ) { - } + // region 4 - clone() { + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - return new this.constructor( this._x, this._y, this._z, this._order ); + } else if ( s1 <= extDet ) { - } + // region 3 - copy( euler ) { + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; + } else { - this._onChangeCallback(); + // region 2 - return this; + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - setFromRotationMatrix( m, order, update ) { + } - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + } else { - const te = m.elements; - const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + // Ray and segment are parallel. - order = order || this._order; + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - switch ( order ) { + } - case 'XYZ': + if ( optionalPointOnRay ) { - this._y = Math.asin( clamp( m13, - 1, 1 ) ); + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - if ( Math.abs( m13 ) < 0.9999999 ) { + } - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); + if ( optionalPointOnSegment ) { - } else { + optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - this._x = Math.atan2( m32, m22 ); - this._z = 0; + } - } + return sqrDist; - break; + } - case 'YXZ': + intersectSphere( sphere, target ) { - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + _vector$a.subVectors( sphere.center, this.origin ); + const tca = _vector$a.dot( this.direction ); + const d2 = _vector$a.dot( _vector$a ) - tca * tca; + const radius2 = sphere.radius * sphere.radius; - if ( Math.abs( m23 ) < 0.9999999 ) { + if ( d2 > radius2 ) return null; - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); + const thc = Math.sqrt( radius2 - d2 ); - } else { + // t0 = first intersect point - entrance on front of sphere + const t0 = tca - thc; - this._y = Math.atan2( - m31, m11 ); - this._z = 0; + // t1 = second intersect point - exit point on back of sphere + const t1 = tca + thc; - } + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; - break; + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, target ); - case 'ZXY': + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, target ); - this._x = Math.asin( clamp( m32, - 1, 1 ) ); + } - if ( Math.abs( m32 ) < 0.9999999 ) { + intersectsSphere( sphere ) { - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); + return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - } else { + } - this._y = 0; - this._z = Math.atan2( m21, m11 ); + distanceToPlane( plane ) { - } + const denominator = plane.normal.dot( this.direction ); - break; + if ( denominator === 0 ) { - case 'ZYX': + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + return 0; - if ( Math.abs( m31 ) < 0.9999999 ) { + } - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); + // Null is preferable to undefined since undefined means.... it is undefined - } else { + return null; - this._x = 0; - this._z = Math.atan2( - m12, m22 ); + } - } + const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - break; + // Return if the ray never intersects the plane - case 'YZX': + return t >= 0 ? t : null; - this._z = Math.asin( clamp( m21, - 1, 1 ) ); + } - if ( Math.abs( m21 ) < 0.9999999 ) { + intersectPlane( plane, target ) { - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); + const t = this.distanceToPlane( plane ); - } else { + if ( t === null ) { - this._x = 0; - this._y = Math.atan2( m13, m33 ); + return null; - } + } - break; + return this.at( t, target ); - case 'XZY': + } - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + intersectsPlane( plane ) { - if ( Math.abs( m12 ) < 0.9999999 ) { + // check if the ray lies on the plane first - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); + const distToPoint = plane.distanceToPoint( this.origin ); - } else { + if ( distToPoint === 0 ) { - this._x = Math.atan2( - m23, m33 ); - this._y = 0; + return true; - } + } - break; + const denominator = plane.normal.dot( this.direction ); - default: + if ( denominator * distToPoint < 0 ) { - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); + return true; } - this._order = order; - - if ( update !== false ) this._onChangeCallback(); + // ray origin is behind the plane (and is pointing behind it) - return this; + return false; } - setFromQuaternion( q, order, update ) { + intersectBox( box, target ) { - _matrix$1.makeRotationFromQuaternion( q ); + let tmin, tmax, tymin, tymax, tzmin, tzmax; - return this.setFromRotationMatrix( _matrix$1, order, update ); + const invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; - } + const origin = this.origin; - setFromVector3( v, order ) { + if ( invdirx >= 0 ) { - return this.set( v.x, v.y, v.z, order || this._order ); + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; - } + } else { - reorder( newOrder ) { + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; - // WARNING: this discards revolution information -bhouston + } - _quaternion$3.setFromEuler( this ); + if ( invdiry >= 0 ) { - return this.setFromQuaternion( _quaternion$3, newOrder ); - - } + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; - equals( euler ) { + } else { - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; - } + } - fromArray( array ) { + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN - this._onChangeCallback(); + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - return this; + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - } + if ( invdirz >= 0 ) { - toArray( array = [], offset = 0 ) { + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; + } else { - return array; + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; - } + } - toVector3( optionalResult ) { + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - if ( optionalResult ) { + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - return optionalResult.set( this._x, this._y, this._z ); + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - } else { + //return point closest to the ray (positive side) - return new Vector3( this._x, this._y, this._z ); + if ( tmax < 0 ) return null; - } + return this.at( tmin >= 0 ? tmin : tmax, target ); } - _onChange( callback ) { - - this._onChangeCallback = callback; + intersectsBox( box ) { - return this; + return this.intersectBox( box, _vector$a ) !== null; } - _onChangeCallback() {} + intersectTriangle( a, b, c, backfaceCulling, target ) { - } + // Compute the offset origin, edges, and normal. - Euler.prototype.isEuler = true; + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - Euler.DefaultOrder = 'XYZ'; - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + _edge1.subVectors( b, a ); + _edge2.subVectors( c, a ); + _normal$1.crossVectors( _edge1, _edge2 ); - class Layers { + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + let DdN = this.direction.dot( _normal$1 ); + let sign; - constructor() { + if ( DdN > 0 ) { - this.mask = 1 | 0; + if ( backfaceCulling ) return null; + sign = 1; - } + } else if ( DdN < 0 ) { - set( channel ) { + sign = - 1; + DdN = - DdN; - this.mask = 1 << channel | 0; + } else { - } + return null; - enable( channel ) { + } - this.mask |= 1 << channel | 0; + _diff.subVectors( this.origin, a ); + const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - } + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { - enableAll() { + return null; - this.mask = 0xffffffff | 0; + } - } + const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - toggle( channel ) { + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { - this.mask ^= 1 << channel | 0; + return null; - } + } - disable( channel ) { + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { - this.mask &= ~ ( 1 << channel | 0 ); + return null; - } + } - disableAll() { + // Line intersects triangle, check if ray does. + const QdN = - sign * _diff.dot( _normal$1 ); - this.mask = 0; + // t < 0, no intersection + if ( QdN < 0 ) { - } + return null; - test( layers ) { + } - return ( this.mask & layers.mask ) !== 0; + // Ray intersects triangle. + return this.at( QdN / DdN, target ); } - } + applyMatrix4( matrix4 ) { - let _object3DId = 0; + this.origin.applyMatrix4( matrix4 ); + this.direction.transformDirection( matrix4 ); - const _v1$4 = new Vector3(); - const _q1 = new Quaternion(); - const _m1$1 = new Matrix4(); - const _target = new Vector3(); + return this; - const _position$3 = new Vector3(); - const _scale$2 = new Vector3(); - const _quaternion$2 = new Quaternion(); + } - const _xAxis = new Vector3( 1, 0, 0 ); - const _yAxis = new Vector3( 0, 1, 0 ); - const _zAxis = new Vector3( 0, 0, 1 ); + equals( ray ) { - const _addedEvent = { type: 'added' }; - const _removedEvent = { type: 'removed' }; + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - class Object3D extends EventDispatcher$1 { + } - constructor() { + clone() { - super(); + return new this.constructor().copy( this ); - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); + } - this.uuid = generateUUID(); + } - this.name = ''; - this.type = 'Object3D'; + class Matrix4 { - this.parent = null; - this.children = []; + constructor() { - this.up = Object3D.DefaultUp.clone(); + this.elements = [ - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - function onRotationChange() { + ]; - quaternion.setFromEuler( rotation, false ); + if ( arguments.length > 0 ) { - } + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - function onQuaternionChange() { + } - rotation.setFromQuaternion( quaternion, undefined, false ); + } - } + set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); + const te = this.elements; - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); + 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; - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); + return this; - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; + } - this.layers = new Layers(); - this.visible = true; + identity() { - this.castShadow = false; - this.receiveShadow = false; + this.set( - this.frustumCulled = true; - this.renderOrder = 0; + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - this.animations = []; + ); - this.userData = {}; + return this; } - onBeforeRender() {} - onAfterRender() {} + clone() { - applyMatrix4( matrix ) { + return new Matrix4().fromArray( this.elements ); - if ( this.matrixAutoUpdate ) this.updateMatrix(); + } - this.matrix.premultiply( matrix ); + copy( m ) { - this.matrix.decompose( this.position, this.quaternion, this.scale ); + const te = this.elements; + const 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; } - applyQuaternion( q ) { + copyPosition( m ) { - this.quaternion.premultiply( q ); + const te = this.elements, me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; return this; } - setRotationFromAxisAngle( axis, angle ) { + setFromMatrix3( m ) { - // assumes axis is normalized + const me = m.elements; - this.quaternion.setFromAxisAngle( axis, angle ); + this.set( - } + me[ 0 ], me[ 3 ], me[ 6 ], 0, + me[ 1 ], me[ 4 ], me[ 7 ], 0, + me[ 2 ], me[ 5 ], me[ 8 ], 0, + 0, 0, 0, 1 - setRotationFromEuler( euler ) { + ); - this.quaternion.setFromEuler( euler, true ); + return this; } - setRotationFromMatrix( m ) { + extractBasis( xAxis, yAxis, zAxis ) { - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + xAxis.setFromMatrixColumn( this, 0 ); + yAxis.setFromMatrixColumn( this, 1 ); + zAxis.setFromMatrixColumn( this, 2 ); - this.quaternion.setFromRotationMatrix( m ); + return this; } - setRotationFromQuaternion( q ) { + makeBasis( xAxis, yAxis, zAxis ) { - // assumes q is normalized + 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 + ); - this.quaternion.copy( q ); + return this; } - rotateOnAxis( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); + extractRotation( m ) { - this.quaternion.multiply( _q1 ); + // this method does not support reflection matrices - return this; + const te = this.elements; + const me = m.elements; - } + const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); + const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); + const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); - rotateOnWorldAxis( axis, angle ) { + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + te[ 3 ] = 0; - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + te[ 7 ] = 0; - _q1.setFromAxisAngle( axis, angle ); + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + te[ 11 ] = 0; - this.quaternion.premultiply( _q1 ); + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; return this; } - rotateX( angle ) { + makeRotationFromEuler( euler ) { - return this.rotateOnAxis( _xAxis, angle ); + if ( ! ( euler && euler.isEuler ) ) { - } + console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - rotateY( angle ) { + } - return this.rotateOnAxis( _yAxis, angle ); + const te = this.elements; - } + const x = euler.x, y = euler.y, z = euler.z; + const a = Math.cos( x ), b = Math.sin( x ); + const c = Math.cos( y ), d = Math.sin( y ); + const e = Math.cos( z ), f = Math.sin( z ); - rotateZ( angle ) { + if ( euler.order === 'XYZ' ) { - return this.rotateOnAxis( _zAxis, angle ); + const ae = a * e, af = a * f, be = b * e, bf = b * f; - } + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; - translateOnAxis( axis, distance ) { + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; - // translate object by distance along axis in object space - // axis is assumed to be normalized + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; - _v1$4.copy( axis ).applyQuaternion( this.quaternion ); + } else if ( euler.order === 'YXZ' ) { - this.position.add( _v1$4.multiplyScalar( distance ) ); + const ce = c * e, cf = c * f, de = d * e, df = d * f; - return this; + 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; - translateX( distance ) { + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; - return this.translateOnAxis( _xAxis, distance ); + } else if ( euler.order === 'ZXY' ) { - } + const ce = c * e, cf = c * f, de = d * e, df = d * f; - translateY( distance ) { + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; - return this.translateOnAxis( _yAxis, distance ); + 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; - translateZ( distance ) { + } else if ( euler.order === 'ZYX' ) { - return this.translateOnAxis( _zAxis, distance ); + const 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; - localToWorld( vector ) { + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; - return vector.applyMatrix4( this.matrixWorld ); + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; - } + } else if ( euler.order === 'YZX' ) { - worldToLocal( vector ) { + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); + 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; - lookAt( x, y, z ) { + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; - // This method does not support objects having non-uniformly-scaled parent(s) + } else if ( euler.order === 'XZY' ) { - if ( x.isVector3 ) { + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - _target.copy( x ); + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; - } else { + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; - _target.set( x, y, z ); + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; } - const parent = this.parent; + // bottom row + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; - this.updateWorldMatrix( true, false ); + // last column + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - _position$3.setFromMatrixPosition( this.matrixWorld ); + return this; - if ( this.isCamera || this.isLight ) { + } - _m1$1.lookAt( _position$3, _target, this.up ); + makeRotationFromQuaternion( q ) { - } else { + return this.compose( _zero, q, _one ); - _m1$1.lookAt( _target, _position$3, this.up ); + } - } + lookAt( eye, target, up ) { - this.quaternion.setFromRotationMatrix( _m1$1 ); + const te = this.elements; - if ( parent ) { + _z.subVectors( eye, target ); - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.invert() ); + if ( _z.lengthSq() === 0 ) { + + // eye and target are in the same position + + _z.z = 1; } - } + _z.normalize(); + _x.crossVectors( up, _z ); - add( object ) { + if ( _x.lengthSq() === 0 ) { - if ( arguments.length > 1 ) { + // up and z are parallel - for ( let i = 0; i < arguments.length; i ++ ) { + if ( Math.abs( up.z ) === 1 ) { - this.add( arguments[ i ] ); + _z.x += 0.0001; + + } else { + + _z.z += 0.0001; } - return this; + _z.normalize(); + _x.crossVectors( up, _z ); } - if ( object === this ) { + _x.normalize(); + _y.crossVectors( _z, _x ); - console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); - return this; + 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; - if ( object && object.isObject3D ) { + } - if ( object.parent !== null ) { + multiply( m, n ) { - object.parent.remove( object ); + if ( n !== undefined ) { - } - - object.parent = this; - this.children.push( object ); - - object.dispatchEvent( _addedEvent ); - - } else { - - console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); } - return this; + return this.multiplyMatrices( this, m ); } - remove( object ) { - - if ( arguments.length > 1 ) { + premultiply( m ) { - for ( let i = 0; i < arguments.length; i ++ ) { + return this.multiplyMatrices( m, this ); - this.remove( arguments[ i ] ); + } - } + multiplyMatrices( a, b ) { - return this; + const ae = a.elements; + const be = b.elements; + const te = this.elements; - } + const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - const index = this.children.indexOf( object ); + const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - if ( index !== - 1 ) { + 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; - object.parent = null; - this.children.splice( index, 1 ); + 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; - object.dispatchEvent( _removedEvent ); + 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; } - clear() { - - for ( let i = 0; i < this.children.length; i ++ ) { - - const object = this.children[ i ]; - - object.parent = null; - - object.dispatchEvent( _removedEvent ); + multiplyScalar( s ) { - } + const te = this.elements; - this.children.length = 0; + 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; - } - attach( object ) { + determinant() { - // adds object as a child of this, while maintaining the object's world transform + const te = this.elements; - this.updateWorldMatrix( true, false ); + const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - _m1$1.copy( this.matrixWorld ).invert(); + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - if ( object.parent !== null ) { + 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 + ) - object.parent.updateWorldMatrix( true, false ); + ); - _m1$1.multiply( object.parent.matrixWorld ); + } - } + transpose() { - object.applyMatrix4( _m1$1 ); + const te = this.elements; + let tmp; - this.add( object ); + 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; - object.updateWorldMatrix( false, true ); + 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; } - getObjectById( id ) { - - return this.getObjectByProperty( 'id', id ); + setPosition( x, y, z ) { - } + const te = this.elements; - getObjectByName( name ) { + if ( x.isVector3 ) { - return this.getObjectByProperty( 'name', name ); + te[ 12 ] = x.x; + te[ 13 ] = x.y; + te[ 14 ] = x.z; - } + } else { - getObjectByProperty( name, value ) { + te[ 12 ] = x; + te[ 13 ] = y; + te[ 14 ] = z; - if ( this[ name ] === value ) return this; + } - for ( let i = 0, l = this.children.length; i < l; i ++ ) { + return this; - const child = this.children[ i ]; - const object = child.getObjectByProperty( name, value ); + } - if ( object !== undefined ) { + invert() { - return object; + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + const te = this.elements, - } + n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], + n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], + n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], + n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 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; - return undefined; + const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - } + if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - getWorldPosition( target ) { + const detInv = 1 / det; - if ( target === undefined ) { + 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; - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); + 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; - this.updateWorldMatrix( true, false ); + 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 target.setFromMatrixPosition( this.matrixWorld ); + return this; } - getWorldQuaternion( target ) { + scale( v ) { - if ( target === undefined ) { + const te = this.elements; + const x = v.x, y = v.y, z = v.z; - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); + 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; - this.updateWorldMatrix( true, false ); + } - this.matrixWorld.decompose( _position$3, target, _scale$2 ); + getMaxScaleOnAxis() { - return target; + const te = this.elements; - } + const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - getWorldScale( target ) { + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - if ( target === undefined ) { + } - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); + makeTranslation( x, y, z ) { - } + this.set( - this.updateWorldMatrix( true, false ); + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 - this.matrixWorld.decompose( _position$3, _quaternion$2, target ); + ); - return target; + return this; } - getWorldDirection( target ) { - - if ( target === undefined ) { + makeRotationX( theta ) { - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); + const c = Math.cos( theta ), s = Math.sin( theta ); - } + this.set( - this.updateWorldMatrix( true, false ); + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 - const e = this.matrixWorld.elements; + ); - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); + return this; } - raycast() {} - - traverse( callback ) { + makeRotationY( theta ) { - callback( this ); + const c = Math.cos( theta ), s = Math.sin( theta ); - const children = this.children; + this.set( - for ( let i = 0, l = children.length; i < l; i ++ ) { + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 - children[ i ].traverse( callback ); + ); - } + return this; } - traverseVisible( callback ) { - - if ( this.visible === false ) return; + makeRotationZ( theta ) { - callback( this ); + const c = Math.cos( theta ), s = Math.sin( theta ); - const children = this.children; + this.set( - for ( let i = 0, l = children.length; i < l; i ++ ) { + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - children[ i ].traverseVisible( callback ); + ); - } + return this; } - traverseAncestors( callback ) { + makeRotationAxis( axis, angle ) { - const parent = this.parent; + // Based on http://www.gamedev.net/reference/articles/article1199.asp - if ( parent !== null ) { + const c = Math.cos( angle ); + const s = Math.sin( angle ); + const t = 1 - c; + const x = axis.x, y = axis.y, z = axis.z; + const tx = t * x, ty = t * y; - callback( parent ); + this.set( - parent.traverseAncestors( callback ); + 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; } - updateMatrix() { + makeScale( x, y, z ) { - this.matrix.compose( this.position, this.quaternion, this.scale ); + this.set( - this.matrixWorldNeedsUpdate = true; + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 - } + ); - updateMatrixWorld( force ) { + return this; - if ( this.matrixAutoUpdate ) this.updateMatrix(); + } - if ( this.matrixWorldNeedsUpdate || force ) { + makeShear( xy, xz, yx, yz, zx, zy ) { - if ( this.parent === null ) { + this.set( - this.matrixWorld.copy( this.matrix ); + 1, yx, zx, 0, + xy, 1, zy, 0, + xz, yz, 1, 0, + 0, 0, 0, 1 - } else { + ); - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + return this; - } + } - this.matrixWorldNeedsUpdate = false; + compose( position, quaternion, scale ) { - force = true; + const te = this.elements; - } + const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; + const x2 = x + x, y2 = y + y, z2 = z + z; + const xx = x * x2, xy = x * y2, xz = x * z2; + const yy = y * y2, yz = y * z2, zz = z * z2; + const wx = w * x2, wy = w * y2, wz = w * z2; - // update children + const sx = scale.x, sy = scale.y, sz = scale.z; - const children = this.children; + te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; + te[ 1 ] = ( xy + wz ) * sx; + te[ 2 ] = ( xz - wy ) * sx; + te[ 3 ] = 0; - for ( let i = 0, l = children.length; i < l; i ++ ) { + te[ 4 ] = ( xy - wz ) * sy; + te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; + te[ 6 ] = ( yz + wx ) * sy; + te[ 7 ] = 0; - children[ i ].updateMatrixWorld( force ); + te[ 8 ] = ( xz + wy ) * sz; + te[ 9 ] = ( yz - wx ) * sz; + te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; + te[ 11 ] = 0; - } + te[ 12 ] = position.x; + te[ 13 ] = position.y; + te[ 14 ] = position.z; + te[ 15 ] = 1; + + return this; } - updateWorldMatrix( updateParents, updateChildren ) { + decompose( position, quaternion, scale ) { - const parent = this.parent; + const te = this.elements; - if ( updateParents === true && parent !== null ) { + let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - parent.updateWorldMatrix( true, false ); + // if determine is negative, we need to invert one scale + const det = this.determinant(); + if ( det < 0 ) sx = - sx; - } + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; - if ( this.matrixAutoUpdate ) this.updateMatrix(); + // scale the rotation part + _m1$2.copy( this ); - if ( this.parent === null ) { + const invSX = 1 / sx; + const invSY = 1 / sy; + const invSZ = 1 / sz; - this.matrixWorld.copy( this.matrix ); + _m1$2.elements[ 0 ] *= invSX; + _m1$2.elements[ 1 ] *= invSX; + _m1$2.elements[ 2 ] *= invSX; - } else { + _m1$2.elements[ 4 ] *= invSY; + _m1$2.elements[ 5 ] *= invSY; + _m1$2.elements[ 6 ] *= invSY; - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + _m1$2.elements[ 8 ] *= invSZ; + _m1$2.elements[ 9 ] *= invSZ; + _m1$2.elements[ 10 ] *= invSZ; - } + quaternion.setFromRotationMatrix( _m1$2 ); - // update children + scale.x = sx; + scale.y = sy; + scale.z = sz; - if ( updateChildren === true ) { + return this; - const children = this.children; + } - for ( let i = 0, l = children.length; i < l; i ++ ) { + makePerspective( left, right, top, bottom, near, far ) { - children[ i ].updateWorldMatrix( false, true ); + if ( far === undefined ) { - } + console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); } - } - - toJSON( meta ) { - - // meta is a string when called from JSON.stringify - const isRootObject = ( meta === undefined || typeof meta === 'string' ); + const te = this.elements; + const x = 2 * near / ( right - left ); + const y = 2 * near / ( top - bottom ); - const output = {}; + const a = ( right + left ) / ( right - left ); + const b = ( top + bottom ) / ( top - bottom ); + const c = - ( far + near ) / ( far - near ); + const d = - 2 * far * near / ( far - near ); - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { + 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; - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {}, - skeletons: {}, - animations: {} - }; + return this; - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; + } - } + makeOrthographic( left, right, top, bottom, near, far ) { - // standard Object3D serialization + const te = this.elements; + const w = 1.0 / ( right - left ); + const h = 1.0 / ( top - bottom ); + const p = 1.0 / ( far - near ); - const object = {}; + const x = ( right + left ) * w; + const y = ( top + bottom ) * h; + const z = ( far + near ) * p; - object.uuid = this.uuid; - object.type = this.type; + 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; - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + return this; - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); + } - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; + equals( matrix ) { - // object specific properties + const te = this.elements; + const me = matrix.elements; - if ( this.isInstancedMesh ) { + for ( let i = 0; i < 16; i ++ ) { - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); + if ( te[ i ] !== me[ i ] ) return false; } - // - - function serialize( library, element ) { + return true; - if ( library[ element.uuid ] === undefined ) { + } - library[ element.uuid ] = element.toJSON( meta ); + fromArray( array, offset = 0 ) { - } + for ( let i = 0; i < 16; i ++ ) { - return element.uuid; + this.elements[ i ] = array[ i + offset ]; } - if ( this.isMesh || this.isLine || this.isPoints ) { + return this; - object.geometry = serialize( meta.geometries, this.geometry ); + } - const parameters = this.geometry.parameters; - - if ( parameters !== undefined && parameters.shapes !== undefined ) { - - const shapes = parameters.shapes; - - if ( Array.isArray( shapes ) ) { - - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - serialize( meta.shapes, shape ); + toArray( array = [], offset = 0 ) { - } + const te = this.elements; - } else { + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; - serialize( meta.shapes, shapes ); + 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; - if ( this.isSkinnedMesh ) { + } - object.bindMode = this.bindMode; - object.bindMatrix = this.bindMatrix.toArray(); + } - if ( this.skeleton !== undefined ) { + Matrix4.prototype.isMatrix4 = true; - serialize( meta.skeletons, this.skeleton ); + const _v1$5 = /*@__PURE__*/ new Vector3(); + const _m1$2 = /*@__PURE__*/ new Matrix4(); + const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); + const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); + const _x = /*@__PURE__*/ new Vector3(); + const _y = /*@__PURE__*/ new Vector3(); + const _z = /*@__PURE__*/ new Vector3(); - object.skeleton = this.skeleton.uuid; + const _matrix$1 = /*@__PURE__*/ new Matrix4(); + const _quaternion$3 = /*@__PURE__*/ new Quaternion(); - } + class Euler { - } + constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - if ( this.material !== undefined ) { + this._x = x; + this._y = y; + this._z = z; + this._order = order; - if ( Array.isArray( this.material ) ) { + } - const uuids = []; + get x() { - for ( let i = 0, l = this.material.length; i < l; i ++ ) { + return this._x; - uuids.push( serialize( meta.materials, this.material[ i ] ) ); + } - } + set x( value ) { - object.material = uuids; + this._x = value; + this._onChangeCallback(); - } else { + } - object.material = serialize( meta.materials, this.material ); + get y() { - } + return this._y; - } + } - // + set y( value ) { - if ( this.children.length > 0 ) { + this._y = value; + this._onChangeCallback(); - object.children = []; + } - for ( let i = 0; i < this.children.length; i ++ ) { + get z() { - object.children.push( this.children[ i ].toJSON( meta ).object ); + return this._z; - } + } - } + set z( value ) { - // + this._z = value; + this._onChangeCallback(); - if ( this.animations.length > 0 ) { + } - object.animations = []; + get order() { - for ( let i = 0; i < this.animations.length; i ++ ) { + return this._order; - const animation = this.animations[ i ]; + } - object.animations.push( serialize( meta.animations, animation ) ); + set order( value ) { - } + this._order = value; + this._onChangeCallback(); - } + } - if ( isRootObject ) { + set( x, y, z, order = this._order ) { - const geometries = extractFromCache( meta.geometries ); - const materials = extractFromCache( meta.materials ); - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - const shapes = extractFromCache( meta.shapes ); - const skeletons = extractFromCache( meta.skeletons ); - const animations = extractFromCache( meta.animations ); + this._x = x; + this._y = y; + this._z = z; + this._order = order; - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; - if ( skeletons.length > 0 ) output.skeletons = skeletons; - if ( animations.length > 0 ) output.animations = animations; + this._onChangeCallback(); - } + return this; - output.object = object; + } - return output; + clone() { - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { + return new this.constructor( this._x, this._y, this._z, this._order ); - const values = []; - for ( const key in cache ) { + } - const data = cache[ key ]; - delete data.metadata; - values.push( data ); + copy( euler ) { - } + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; - return values; + this._onChangeCallback(); - } + return this; } - clone( recursive ) { - - return new this.constructor().copy( this, recursive ); + setFromRotationMatrix( m, order = this._order, update = true ) { - } + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - copy( source, recursive = true ) { + const te = m.elements; + const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - this.name = source.name; + switch ( order ) { - this.up.copy( source.up ); + case 'XYZ': - this.position.copy( source.position ); - this.rotation.order = source.rotation.order; - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); + this._y = Math.asin( clamp( m13, - 1, 1 ) ); - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); + if ( Math.abs( m13 ) < 0.9999999 ) { - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); - this.layers.mask = source.layers.mask; - this.visible = source.visible; + } else { - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; + this._x = Math.atan2( m32, m22 ); + this._z = 0; - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; + } - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + break; - if ( recursive === true ) { + case 'YXZ': - for ( let i = 0; i < source.children.length; i ++ ) { + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - const child = source.children[ i ]; - this.add( child.clone() ); + if ( Math.abs( m23 ) < 0.9999999 ) { - } + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); - } + } else { - return this; + this._y = Math.atan2( - m31, m11 ); + this._z = 0; - } + } - } + break; - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; + case 'ZXY': - Object3D.prototype.isObject3D = true; + this._x = Math.asin( clamp( m32, - 1, 1 ) ); - const _vector1 = /*@__PURE__*/ new Vector3(); - const _vector2$1 = /*@__PURE__*/ new Vector3(); - const _normalMatrix = /*@__PURE__*/ new Matrix3(); + if ( Math.abs( m32 ) < 0.9999999 ) { - class Plane { + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); - constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { + } else { - // normal is assumed to be normalized + this._y = 0; + this._z = Math.atan2( m21, m11 ); - this.normal = normal; - this.constant = constant; + } - } + break; - set( normal, constant ) { + case 'ZYX': - this.normal.copy( normal ); - this.constant = constant; + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - return this; + if ( Math.abs( m31 ) < 0.9999999 ) { - } + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); - setComponents( x, y, z, w ) { + } else { - this.normal.set( x, y, z ); - this.constant = w; + this._x = 0; + this._z = Math.atan2( - m12, m22 ); - return this; + } - } + break; - setFromNormalAndCoplanarPoint( normal, point ) { + case 'YZX': - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); + this._z = Math.asin( clamp( m21, - 1, 1 ) ); - return this; + if ( Math.abs( m21 ) < 0.9999999 ) { - } + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); - setFromCoplanarPoints( a, b, c ) { + } else { - const normal = _vector1.subVectors( c, b ).cross( _vector2$1.subVectors( a, b ) ).normalize(); + this._x = 0; + this._y = Math.atan2( m13, m33 ); - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + } - this.setFromNormalAndCoplanarPoint( normal, a ); + break; - return this; + case 'XZY': - } + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - copy( plane ) { + if ( Math.abs( m12 ) < 0.9999999 ) { - this.normal.copy( plane.normal ); - this.constant = plane.constant; + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); - return this; + } else { - } + this._x = Math.atan2( - m23, m33 ); + this._y = 0; - normalize() { + } - // Note: will lead to a divide by zero if the plane is invalid. + break; - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; + default: - return this; + console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - } + } - negate() { + this._order = order; - this.constant *= - 1; - this.normal.negate(); + if ( update === true ) this._onChangeCallback(); return this; } - distanceToPoint( point ) { + setFromQuaternion( q, order, update ) { - return this.normal.dot( point ) + this.constant; + _matrix$1.makeRotationFromQuaternion( q ); + + return this.setFromRotationMatrix( _matrix$1, order, update ); } - distanceToSphere( sphere ) { + setFromVector3( v, order = this._order ) { - return this.distanceToPoint( sphere.center ) - sphere.radius; + return this.set( v.x, v.y, v.z, order ); } - projectPoint( point, target ) { - - if ( target === undefined ) { + reorder( newOrder ) { - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); + // WARNING: this discards revolution information -bhouston - } + _quaternion$3.setFromEuler( this ); - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + return this.setFromQuaternion( _quaternion$3, newOrder ); } - intersectLine( line, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); + equals( euler ) { - } + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - const direction = line.delta( _vector1 ); + } - const denominator = this.normal.dot( direction ); + fromArray( array ) { - if ( denominator === 0 ) { + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { + this._onChangeCallback(); - return target.copy( line.start ); + return this; - } + } - // Unsure if this is the correct method to handle this case. - return null; + toArray( array = [], offset = 0 ) { - } + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._order; - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + return array; - if ( t < 0 || t > 1 ) { + } - return null; + _onChange( callback ) { - } + this._onChangeCallback = callback; - return target.copy( direction ).multiplyScalar( t ).add( line.start ); + return this; } - intersectsLine( line ) { + _onChangeCallback() {} - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + } - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); + Euler.prototype.isEuler = true; - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + Euler.DefaultOrder = 'XYZ'; + Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - } + class Layers { - intersectsBox( box ) { + constructor() { - return box.intersectsPlane( this ); + this.mask = 1 | 0; } - intersectsSphere( sphere ) { + set( channel ) { - return sphere.intersectsPlane( this ); + this.mask = ( 1 << channel | 0 ) >>> 0; } - coplanarPoint( target ) { + enable( channel ) { - if ( target === undefined ) { + this.mask |= 1 << channel | 0; - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); + } - } + enableAll() { - return target.copy( this.normal ).multiplyScalar( - this.constant ); + this.mask = 0xffffffff | 0; } - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); + toggle( channel ) { - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); + this.mask ^= 1 << channel | 0; - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + } - this.constant = - referencePoint.dot( normal ); + disable( channel ) { - return this; + this.mask &= ~ ( 1 << channel | 0 ); } - translate( offset ) { - - this.constant -= offset.dot( this.normal ); + disableAll() { - return this; + this.mask = 0; } - equals( plane ) { + test( layers ) { - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + return ( this.mask & layers.mask ) !== 0; } - clone() { + isEnabled( channel ) { - return new this.constructor().copy( this ); + return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } } - Plane.prototype.isPlane = true; - - const _v0$1 = /*@__PURE__*/ new Vector3(); - const _v1$3 = /*@__PURE__*/ new Vector3(); - const _v2$2 = /*@__PURE__*/ new Vector3(); - const _v3$1 = /*@__PURE__*/ new Vector3(); - - const _vab = /*@__PURE__*/ new Vector3(); - const _vac = /*@__PURE__*/ new Vector3(); - const _vbc = /*@__PURE__*/ new Vector3(); - const _vap = /*@__PURE__*/ new Vector3(); - const _vbp = /*@__PURE__*/ new Vector3(); - const _vcp = /*@__PURE__*/ new Vector3(); - - class Triangle { - - constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { - - this.a = a; - this.b = b; - this.c = c; - - } - - static getNormal( a, b, c, target ) { + let _object3DId = 0; - if ( target === undefined ) { + const _v1$4 = /*@__PURE__*/ new Vector3(); + const _q1 = /*@__PURE__*/ new Quaternion(); + const _m1$1 = /*@__PURE__*/ new Matrix4(); + const _target = /*@__PURE__*/ new Vector3(); - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); + const _position$3 = /*@__PURE__*/ new Vector3(); + const _scale$2 = /*@__PURE__*/ new Vector3(); + const _quaternion$2 = /*@__PURE__*/ new Quaternion(); - } + const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); + const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); + const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); + const _addedEvent = { type: 'added' }; + const _removedEvent = { type: 'removed' }; - const targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { + class Object3D extends EventDispatcher$1 { - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); + constructor() { - } + super(); - return target.set( 0, 0, 0 ); + Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - } + this.uuid = generateUUID(); - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - static getBarycoord( point, a, b, c, target ) { + this.name = ''; + this.type = 'Object3D'; - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$2.subVectors( point, a ); + this.parent = null; + this.children = []; - const dot00 = _v0$1.dot( _v0$1 ); - const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$2 ); - const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$2 ); + this.up = Object3D.DefaultUp.clone(); - const denom = ( dot00 * dot11 - dot01 * dot01 ); + const position = new Vector3(); + const rotation = new Euler(); + const quaternion = new Quaternion(); + const scale = new Vector3( 1, 1, 1 ); - if ( target === undefined ) { + function onRotationChange() { - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); + quaternion.setFromEuler( rotation, false ); } - // collinear or singular triangle - if ( denom === 0 ) { + function onQuaternionChange() { - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); + rotation.setFromQuaternion( quaternion, undefined, false ); } - const invDenom = 1 / denom; - const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); - - } - - static containsPoint( point, a, b, c ) { - - this.getBarycoord( point, a, b, c, _v3$1 ); - - return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); - - } - - static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { + rotation._onChange( onRotationChange ); + quaternion._onChange( onQuaternionChange ); - this.getBarycoord( point, p1, p2, p3, _v3$1 ); - - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3$1.x ); - target.addScaledVector( uv2, _v3$1.y ); - target.addScaledVector( uv3, _v3$1.z ); - - return target; - - } + Object.defineProperties( this, { + position: { + configurable: true, + enumerable: true, + value: position + }, + rotation: { + configurable: true, + enumerable: true, + value: rotation + }, + quaternion: { + configurable: true, + enumerable: true, + value: quaternion + }, + scale: { + configurable: true, + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new Matrix4() + }, + normalMatrix: { + value: new Matrix3() + } + } ); - static isFrontFacing( a, b, c, direction ) { + this.matrix = new Matrix4(); + this.matrixWorld = new Matrix4(); - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); + this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; + this.layers = new Layers(); + this.visible = true; - } + this.castShadow = false; + this.receiveShadow = false; - set( a, b, c ) { + this.frustumCulled = true; + this.renderOrder = 0; - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); + this.animations = []; - return this; + this.userData = {}; } - setFromPointsAndIndices( points, i0, i1, i2 ) { + onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); + onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} - return this; + applyMatrix4( matrix ) { - } + if ( this.matrixAutoUpdate ) this.updateMatrix(); - clone() { + this.matrix.premultiply( matrix ); - return new this.constructor().copy( this ); + this.matrix.decompose( this.position, this.quaternion, this.scale ); } - copy( triangle ) { + applyQuaternion( q ) { - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); + this.quaternion.premultiply( q ); return this; } - getArea() { + setRotationFromAxisAngle( axis, angle ) { - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); + // assumes axis is normalized - return _v0$1.cross( _v1$3 ).length() * 0.5; + this.quaternion.setFromAxisAngle( axis, angle ); } - getMidpoint( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); - - } + setRotationFromEuler( euler ) { - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + this.quaternion.setFromEuler( euler, true ); } - getNormal( target ) { - - return Triangle.getNormal( this.a, this.b, this.c, target ); + setRotationFromMatrix( m ) { - } + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - getPlane( target ) { + this.quaternion.setFromRotationMatrix( m ); - if ( target === undefined ) { + } - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); + setRotationFromQuaternion( q ) { - } + // assumes q is normalized - return target.setFromCoplanarPoints( this.a, this.b, this.c ); + this.quaternion.copy( q ); } - getBarycoord( point, target ) { + rotateOnAxis( axis, angle ) { - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); + // rotate object on axis in object space + // axis is assumed to be normalized - } + _q1.setFromAxisAngle( axis, angle ); - getUV( point, uv1, uv2, uv3, target ) { + this.quaternion.multiply( _q1 ); - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); + return this; } - containsPoint( point ) { + rotateOnWorldAxis( axis, angle ) { - return Triangle.containsPoint( point, this.a, this.b, this.c ); + // rotate object on axis in world space + // axis is assumed to be normalized + // method assumes no rotated parent - } + _q1.setFromAxisAngle( axis, angle ); - isFrontFacing( direction ) { + this.quaternion.premultiply( _q1 ); - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); + return this; } - intersectsBox( box ) { + rotateX( angle ) { - return box.intersectsTriangle( this ); + return this.rotateOnAxis( _xAxis, angle ); } - closestPointToPoint( p, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } + rotateY( angle ) { - const a = this.a, b = this.b, c = this.c; - let v, w; + return this.rotateOnAxis( _yAxis, angle ); - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. + } - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - const d1 = _vab.dot( _vap ); - const d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { + rotateZ( angle ) { - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); + return this.rotateOnAxis( _zAxis, angle ); - } + } - _vbp.subVectors( p, b ); - const d3 = _vab.dot( _vbp ); - const d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { + translateOnAxis( axis, distance ) { - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); + // translate object by distance along axis in object space + // axis is assumed to be normalized - } + _v1$4.copy( axis ).applyQuaternion( this.quaternion ); - const vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { + this.position.add( _v1$4.multiplyScalar( distance ) ); - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); + return this; - } + } - _vcp.subVectors( p, c ); - const d5 = _vab.dot( _vcp ); - const d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { + translateX( distance ) { - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); + return this.translateOnAxis( _xAxis, distance ); - } + } - const vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { + translateY( distance ) { - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); + return this.translateOnAxis( _yAxis, distance ); - } + } - const va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { + translateZ( distance ) { - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC + return this.translateOnAxis( _zAxis, distance ); - } + } - // face region - const denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; + localToWorld( vector ) { - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); + return vector.applyMatrix4( this.matrixWorld ); } - equals( triangle ) { + worldToLocal( vector ) { - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } - } + lookAt( x, y, z ) { - let materialId = 0; + // This method does not support objects having non-uniformly-scaled parent(s) - function Material() { + if ( x.isVector3 ) { - Object.defineProperty( this, 'id', { value: materialId ++ } ); + _target.copy( x ); - this.uuid = generateUUID(); + } else { - this.name = ''; - this.type = 'Material'; + _target.set( x, y, z ); - this.fog = true; + } - this.blending = NormalBlending; - this.side = FrontSide; - this.vertexColors = false; + const parent = this.parent; - this.opacity = 1; - this.transparent = false; + this.updateWorldMatrix( true, false ); - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; + _position$3.setFromMatrixPosition( this.matrixWorld ); - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; + if ( this.isCamera || this.isLight ) { - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; + _m1$1.lookAt( _position$3, _target, this.up ); - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; + } else { - this.shadowSide = null; + _m1$1.lookAt( _target, _position$3, this.up ); - this.colorWrite = true; + } - this.precision = null; // override the renderer's default precision for this material + this.quaternion.setFromRotationMatrix( _m1$1 ); - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; + if ( parent ) { - this.dithering = false; + _m1$1.extractRotation( parent.matrixWorld ); + _q1.setFromRotationMatrix( _m1$1 ); + this.quaternion.premultiply( _q1.invert() ); - this.alphaTest = 0; - this.alphaToCoverage = false; - this.premultipliedAlpha = false; + } - this.visible = true; + } - this.toneMapped = true; + add( object ) { - this.userData = {}; + if ( arguments.length > 1 ) { - this.version = 0; + for ( let i = 0; i < arguments.length; i ++ ) { - } + this.add( arguments[ i ] ); - Material.prototype = Object.assign( Object.create( EventDispatcher$1.prototype ), { + } - constructor: Material, + return this; - isMaterial: true, + } - onBuild: function ( /* shaderobject, renderer */ ) {}, + if ( object === this ) { - onBeforeCompile: function ( /* shaderobject, renderer */ ) {}, + console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); + return this; - customProgramCacheKey: function () { + } - return this.onBeforeCompile.toString(); + if ( object && object.isObject3D ) { - }, + if ( object.parent !== null ) { - setValues: function ( values ) { + object.parent.remove( object ); - if ( values === undefined ) return; + } - for ( const key in values ) { + object.parent = this; + this.children.push( object ); - const newValue = values[ key ]; + object.dispatchEvent( _addedEvent ); - if ( newValue === undefined ) { + } else { - console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); - continue; + console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); - } + } - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { + return this; - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; + } - } + remove( object ) { - const currentValue = this[ key ]; + if ( arguments.length > 1 ) { - if ( currentValue === undefined ) { + for ( let i = 0; i < arguments.length; i ++ ) { - console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); - continue; + this.remove( arguments[ i ] ); } - if ( currentValue && currentValue.isColor ) { - - currentValue.set( newValue ); + return this; - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { + } - currentValue.copy( newValue ); + const index = this.children.indexOf( object ); - } else { + if ( index !== - 1 ) { - this[ key ] = newValue; + object.parent = null; + this.children.splice( index, 1 ); - } + object.dispatchEvent( _removedEvent ); } - }, - - toJSON: function ( meta ) { + return this; - const isRoot = ( meta === undefined || typeof meta === 'string' ); + } - if ( isRoot ) { + removeFromParent() { - meta = { - textures: {}, - images: {} - }; + const parent = this.parent; - } + if ( parent !== null ) { - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; + parent.remove( this ); - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; + } - if ( this.name !== '' ) data.name = this.name; + return this; - 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; + clear() { - if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; + for ( let i = 0; i < this.children.length; i ++ ) { - 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; + const object = this.children[ i ]; - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { + object.parent = null; - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; + object.dispatchEvent( _removedEvent ); } - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { + this.children.length = 0; - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; + return this; - } - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { + } - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); + attach( object ) { - } + // adds object as a child of this, while maintaining the object's world transform - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) - if ( this.lightMap && this.lightMap.isTexture ) { + this.updateWorldMatrix( true, false ); - data.lightMap = this.lightMap.toJSON( meta ).uuid; - data.lightMapIntensity = this.lightMapIntensity; + _m1$1.copy( this.matrixWorld ).invert(); - } + if ( object.parent !== null ) { - if ( this.aoMap && this.aoMap.isTexture ) { + object.parent.updateWorldMatrix( true, false ); - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; + _m1$1.multiply( object.parent.matrixWorld ); } - if ( this.bumpMap && this.bumpMap.isTexture ) { + object.applyMatrix4( _m1$1 ); - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; + this.add( object ); - } + object.updateWorldMatrix( false, true ); - if ( this.normalMap && this.normalMap.isTexture ) { + return this; - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); + } - } + getObjectById( id ) { - if ( this.displacementMap && this.displacementMap.isTexture ) { + return this.getObjectByProperty( 'id', id ); - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; + } - } + getObjectByName( name ) { - 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; + return this.getObjectByProperty( 'name', name ); - 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 ) { + getObjectByProperty( name, value ) { - data.envMap = this.envMap.toJSON( meta ).uuid; + if ( this[ name ] === value ) return this; - if ( this.combine !== undefined ) data.combine = this.combine; + for ( let i = 0, l = this.children.length; i < l; i ++ ) { - } + const child = this.children[ i ]; + const object = child.getObjectByProperty( name, value ); - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; - if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; + if ( object !== undefined ) { - if ( this.gradientMap && this.gradientMap.isTexture ) { + return object; - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; + } } - if ( this.size !== undefined ) data.size = this.size; - if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + return undefined; - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; + } - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; + getWorldPosition( target ) { - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - data.colorWrite = this.colorWrite; + this.updateWorldMatrix( true, false ); - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; + return target.setFromMatrixPosition( this.matrixWorld ); - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; + } - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; + getWorldQuaternion( target ) { - if ( this.linewidth && 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; + this.updateWorldMatrix( true, false ); - if ( this.dithering === true ) data.dithering = true; + this.matrixWorld.decompose( _position$3, target, _scale$2 ); - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; + return target; - 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.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; + getWorldScale( target ) { - if ( this.flatShading === true ) data.flatShading = this.flatShading; + this.updateWorldMatrix( true, false ); - if ( this.visible === false ) data.visible = false; + this.matrixWorld.decompose( _position$3, _quaternion$2, target ); - if ( this.toneMapped === false ) data.toneMapped = false; + return target; - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; + } - // TODO: Copied from Object3D.toJSON + getWorldDirection( target ) { - function extractFromCache( cache ) { + this.updateWorldMatrix( true, false ); - const values = []; + const e = this.matrixWorld.elements; - for ( const key in cache ) { + return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - const data = cache[ key ]; - delete data.metadata; - values.push( data ); + } - } + raycast( /* raycaster, intersects */ ) {} - return values; + traverse( callback ) { - } + callback( this ); - if ( isRoot ) { + const children = this.children; - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; + children[ i ].traverse( callback ); } - return data; + } - }, + traverseVisible( callback ) { - clone: function () { + if ( this.visible === false ) return; - return new this.constructor().copy( this ); + callback( this ); - }, + const children = this.children; - copy: function ( source ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - this.name = source.name; - - this.fog = source.fog; - - this.blending = source.blending; - this.side = source.side; - this.vertexColors = source.vertexColors; - - this.opacity = source.opacity; - this.transparent = source.transparent; + children[ i ].traverseVisible( callback ); - 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.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; + traverseAncestors( callback ) { - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; + const parent = this.parent; - if ( srcPlanes !== null ) { + if ( parent !== null ) { - const n = srcPlanes.length; - dstPlanes = new Array( n ); + callback( parent ); - for ( let i = 0; i !== n; ++ i ) { + parent.traverseAncestors( callback ); - dstPlanes[ i ] = srcPlanes[ i ].clone(); + } - } + } - } + updateMatrix() { - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; + this.matrix.compose( this.position, this.quaternion, this.scale ); - this.shadowSide = source.shadowSide; + this.matrixWorldNeedsUpdate = true; - this.colorWrite = source.colorWrite; + } - this.precision = source.precision; + updateMatrixWorld( force ) { - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; + if ( this.matrixAutoUpdate ) this.updateMatrix(); - this.dithering = source.dithering; + if ( this.matrixWorldNeedsUpdate || force ) { - this.alphaTest = source.alphaTest; - this.alphaToCoverage = source.alphaToCoverage; - this.premultipliedAlpha = source.premultipliedAlpha; + if ( this.parent === null ) { - this.visible = source.visible; + this.matrixWorld.copy( this.matrix ); - this.toneMapped = source.toneMapped; + } else { - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - return this; + } - }, + this.matrixWorldNeedsUpdate = false; - dispose: function () { + force = true; - this.dispatchEvent( { type: 'dispose' } ); + } - } + // update children - } ); + const children = this.children; - Object.defineProperty( Material.prototype, 'needsUpdate', { + for ( let i = 0, l = children.length; i < l; i ++ ) { - set: function ( value ) { + children[ i ].updateMatrixWorld( force ); - if ( value === true ) this.version ++; + } } - } ); + updateWorldMatrix( updateParents, updateChildren ) { - const _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 }; + const parent = this.parent; - const _hslA = { h: 0, s: 0, l: 0 }; - const _hslB = { h: 0, s: 0, l: 0 }; + if ( updateParents === true && parent !== null ) { - function hue2rgb( p, q, t ) { + parent.updateWorldMatrix( true, false ); - 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; + } - } + if ( this.matrixAutoUpdate ) this.updateMatrix(); - function SRGBToLinear( c ) { + if ( this.parent === null ) { - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); + this.matrixWorld.copy( this.matrix ); - } + } else { - function LinearToSRGB( c ) { + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; + } - } + // update children - class Color { + if ( updateChildren === true ) { - constructor( r, g, b ) { + const children = this.children; - if ( g === undefined && b === undefined ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - // r is THREE.Color, hex or string - return this.set( r ); + children[ i ].updateWorldMatrix( false, true ); - } + } - return this.setRGB( r, g, b ); + } } - set( value ) { - - if ( value && value.isColor ) { + toJSON( meta ) { - this.copy( value ); + // meta is a string when called from JSON.stringify + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - } else if ( typeof value === 'number' ) { + const output = {}; - this.setHex( value ); + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { - } else if ( typeof value === 'string' ) { + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {}, + shapes: {}, + skeletons: {}, + animations: {}, + nodes: {} + }; - this.setStyle( value ); + output.metadata = { + version: 4.5, + type: 'Object', + generator: 'Object3D.toJSON' + }; } - return this; - - } + // standard Object3D serialization - setScalar( scalar ) { + const object = {}; - this.r = scalar; - this.g = scalar; - this.b = scalar; + object.uuid = this.uuid; + object.type = this.type; - return this; + if ( this.name !== '' ) object.name = this.name; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; + if ( this.frustumCulled === false ) object.frustumCulled = false; + if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; + if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; - } + object.layers = this.layers.mask; + object.matrix = this.matrix.toArray(); - setHex( hex ) { + if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - hex = Math.floor( hex ); + // object specific properties - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; + if ( this.isInstancedMesh ) { - return this; + object.type = 'InstancedMesh'; + object.count = this.count; + object.instanceMatrix = this.instanceMatrix.toJSON(); + if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); - } + } - setRGB( r, g, b ) { + // - this.r = r; - this.g = g; - this.b = b; + function serialize( library, element ) { - return this; + if ( library[ element.uuid ] === undefined ) { - } + library[ element.uuid ] = element.toJSON( meta ); - setHSL( h, s, l ) { + } - // h,s,l ranges are in 0.0 - 1.0 - h = euclideanModulo( h, 1 ); - s = clamp( s, 0, 1 ); - l = clamp( l, 0, 1 ); + return element.uuid; - if ( s === 0 ) { + } - this.r = this.g = this.b = l; + if ( this.isScene ) { - } else { + if ( this.background ) { - const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - const q = ( 2 * l ) - p; + if ( this.background.isColor ) { - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); + object.background = this.background.toJSON(); - } + } else if ( this.background.isTexture ) { - return this; + object.background = this.background.toJSON( meta ).uuid; - } + } - setStyle( style ) { + } - function handleAlpha( string ) { + if ( this.environment && this.environment.isTexture ) { - if ( string === undefined ) return; + object.environment = this.environment.toJSON( meta ).uuid; - if ( parseFloat( string ) < 1 ) { + } - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + } else if ( this.isMesh || this.isLine || this.isPoints ) { - } + object.geometry = serialize( meta.geometries, this.geometry ); - } + const parameters = this.geometry.parameters; + if ( parameters !== undefined && parameters.shapes !== undefined ) { - let m; + const shapes = parameters.shapes; - if ( m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec( style ) ) { + if ( Array.isArray( shapes ) ) { - // rgb / hsl + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - let color; - const name = m[ 1 ]; - const components = m[ 2 ]; + const shape = shapes[ i ]; - switch ( name ) { + serialize( meta.shapes, shape ); - case 'rgb': - case 'rgba': + } - if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { + } else { - // 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; + serialize( meta.shapes, shapes ); - handleAlpha( color[ 4 ] ); + } - return this; + } - } + } - if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { + if ( this.isSkinnedMesh ) { - // 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; + object.bindMode = this.bindMode; + object.bindMatrix = this.bindMatrix.toArray(); - handleAlpha( color[ 4 ] ); + if ( this.skeleton !== undefined ) { - return this; + serialize( meta.skeletons, this.skeleton ); - } + object.skeleton = this.skeleton.uuid; - break; + } - case 'hsl': - case 'hsla': + } - if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { + if ( this.material !== undefined ) { - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - const h = parseFloat( color[ 1 ] ) / 360; - const s = parseInt( color[ 2 ], 10 ) / 100; - const l = parseInt( color[ 3 ], 10 ) / 100; + if ( Array.isArray( this.material ) ) { - handleAlpha( color[ 4 ] ); + const uuids = []; - return this.setHSL( h, s, l ); + for ( let i = 0, l = this.material.length; i < l; i ++ ) { - } + uuids.push( serialize( meta.materials, this.material[ i ] ) ); - break; + } - } + object.material = uuids; - } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { + } else { - // hex color + object.material = serialize( meta.materials, this.material ); - const hex = m[ 1 ]; - const 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; + if ( this.children.length > 0 ) { - } else if ( size === 6 ) { + object.children = []; - // #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; + for ( let i = 0; i < this.children.length; i ++ ) { - return this; + object.children.push( this.children[ i ].toJSON( meta ).object ); } } - if ( style && style.length > 0 ) { + // - return this.setColorName( style ); + if ( this.animations.length > 0 ) { - } + object.animations = []; - return this; + for ( let i = 0; i < this.animations.length; i ++ ) { - } + const animation = this.animations[ i ]; - setColorName( style ) { + object.animations.push( serialize( meta.animations, animation ) ); - // color keywords - const hex = _colorKeywords[ style.toLowerCase() ]; + } - if ( hex !== undefined ) { + } - // red - this.setHex( hex ); + if ( isRootObject ) { - } else { + const geometries = extractFromCache( meta.geometries ); + const materials = extractFromCache( meta.materials ); + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); + const shapes = extractFromCache( meta.shapes ); + const skeletons = extractFromCache( meta.skeletons ); + const animations = extractFromCache( meta.animations ); + const nodes = extractFromCache( meta.nodes ); - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + if ( shapes.length > 0 ) output.shapes = shapes; + if ( skeletons.length > 0 ) output.skeletons = skeletons; + if ( animations.length > 0 ) output.animations = animations; + if ( nodes.length > 0 ) output.nodes = nodes; } - return this; + output.object = object; - } + return output; - clone() { + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache( cache ) { - return new this.constructor( this.r, this.g, this.b ); + const values = []; + for ( const key in cache ) { - } + const data = cache[ key ]; + delete data.metadata; + values.push( data ); - copy( color ) { + } - this.r = color.r; - this.g = color.g; - this.b = color.b; + return values; - return this; + } } - copyGammaToLinear( color, gammaFactor = 2.0 ) { - - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); + clone( recursive ) { - return this; + return new this.constructor().copy( this, recursive ); } - copyLinearToGamma( color, gammaFactor = 2.0 ) { + copy( source, recursive = true ) { - const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; + this.name = source.name; - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); + this.up.copy( source.up ); - return this; + this.position.copy( source.position ); + this.rotation.order = source.rotation.order; + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); - } + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); - convertGammaToLinear( gammaFactor ) { + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - this.copyGammaToLinear( this, gammaFactor ); + this.layers.mask = source.layers.mask; + this.visible = source.visible; - return this; + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; - } + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; - convertLinearToGamma( gammaFactor ) { + this.userData = JSON.parse( JSON.stringify( source.userData ) ); - this.copyLinearToGamma( this, gammaFactor ); + if ( recursive === true ) { - return this; + for ( let i = 0; i < source.children.length; i ++ ) { - } + const child = source.children[ i ]; + this.add( child.clone() ); - copySRGBToLinear( color ) { + } - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); + } return this; } - copyLinearToSRGB( color ) { + } - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); + Object3D.DefaultUp = new Vector3( 0, 1, 0 ); + Object3D.DefaultMatrixAutoUpdate = true; - return this; + Object3D.prototype.isObject3D = true; - } + const _v0$1 = /*@__PURE__*/ new Vector3(); + const _v1$3 = /*@__PURE__*/ new Vector3(); + const _v2$2 = /*@__PURE__*/ new Vector3(); + const _v3$1 = /*@__PURE__*/ new Vector3(); - convertSRGBToLinear() { + const _vab = /*@__PURE__*/ new Vector3(); + const _vac = /*@__PURE__*/ new Vector3(); + const _vbc = /*@__PURE__*/ new Vector3(); + const _vap = /*@__PURE__*/ new Vector3(); + const _vbp = /*@__PURE__*/ new Vector3(); + const _vcp = /*@__PURE__*/ new Vector3(); - this.copySRGBToLinear( this ); + class Triangle { - return this; + constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { + + this.a = a; + this.b = b; + this.c = c; } - convertLinearToSRGB() { + static getNormal( a, b, c, target ) { - this.copyLinearToSRGB( this ); + target.subVectors( c, b ); + _v0$1.subVectors( a, b ); + target.cross( _v0$1 ); - return this; + const targetLengthSq = target.lengthSq(); + if ( targetLengthSq > 0 ) { - } + return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - getHex() { + } - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + return target.set( 0, 0, 0 ); } - getHexString() { - - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + static getBarycoord( point, a, b, c, target ) { - } + _v0$1.subVectors( c, a ); + _v1$3.subVectors( b, a ); + _v2$2.subVectors( point, a ); - getHSL( target ) { + const dot00 = _v0$1.dot( _v0$1 ); + const dot01 = _v0$1.dot( _v1$3 ); + const dot02 = _v0$1.dot( _v2$2 ); + const dot11 = _v1$3.dot( _v1$3 ); + const dot12 = _v1$3.dot( _v2$2 ); - // h,s,l ranges are in 0.0 - 1.0 + const denom = ( dot00 * dot11 - dot01 * dot01 ); - if ( target === undefined ) { + // collinear or singular triangle + if ( denom === 0 ) { - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return target.set( - 2, - 1, - 1 ); } - const r = this.r, g = this.g, b = this.b; + const invDenom = 1 / denom; + const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - const max = Math.max( r, g, b ); - const min = Math.min( r, g, b ); + // barycentric coordinates must always sum to 1 + return target.set( 1 - u - v, v, u ); - let hue, saturation; - const lightness = ( min + max ) / 2.0; - - if ( min === max ) { - - hue = 0; - saturation = 0; - - } else { - - const delta = max - min; + } - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + static containsPoint( point, a, b, c ) { - switch ( max ) { + this.getBarycoord( point, a, b, c, _v3$1 ); - 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; + return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); - } + } - hue /= 6; + static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - } + this.getBarycoord( point, p1, p2, p3, _v3$1 ); - target.h = hue; - target.s = saturation; - target.l = lightness; + target.set( 0, 0 ); + target.addScaledVector( uv1, _v3$1.x ); + target.addScaledVector( uv2, _v3$1.y ); + target.addScaledVector( uv3, _v3$1.z ); return target; } - getStyle() { - - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + static isFrontFacing( a, b, c, direction ) { - } + _v0$1.subVectors( c, b ); + _v1$3.subVectors( a, b ); - offsetHSL( h, s, l ) { + // strictly front facing + return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - this.getHSL( _hslA ); + } - _hslA.h += h; _hslA.s += s; _hslA.l += l; + set( a, b, c ) { - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); return this; } - add( color ) { + setFromPointsAndIndices( points, i0, i1, i2 ) { - this.r += color.r; - this.g += color.g; - this.b += color.b; + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); return this; } - addColors( color1, color2 ) { + setFromAttributeAndIndices( attribute, i0, i1, i2 ) { - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; + this.a.fromBufferAttribute( attribute, i0 ); + this.b.fromBufferAttribute( attribute, i1 ); + this.c.fromBufferAttribute( attribute, i2 ); return this; } - addScalar( s ) { - - this.r += s; - this.g += s; - this.b += s; + clone() { - return this; + return new this.constructor().copy( this ); } - sub( color ) { + copy( triangle ) { - 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 ); + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); return this; } - multiply( color ) { + getArea() { - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; + _v0$1.subVectors( this.c, this.b ); + _v1$3.subVectors( this.a, this.b ); - return this; + return _v0$1.cross( _v1$3 ).length() * 0.5; } - multiplyScalar( s ) { - - this.r *= s; - this.g *= s; - this.b *= s; + getMidpoint( target ) { - return this; + return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); } - lerp( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; + getNormal( target ) { - return this; + return Triangle.getNormal( this.a, this.b, this.c, target ); } - lerpColors( color1, color2, alpha ) { - - this.r = color1.r + ( color2.r - color1.r ) * alpha; - this.g = color1.g + ( color2.g - color1.g ) * alpha; - this.b = color1.b + ( color2.b - color1.b ) * alpha; + getPlane( target ) { - return this; + return target.setFromCoplanarPoints( this.a, this.b, this.c ); } - lerpHSL( color, alpha ) { + getBarycoord( point, target ) { - this.getHSL( _hslA ); - color.getHSL( _hslB ); + return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - const h = lerp( _hslA.h, _hslB.h, alpha ); - const s = lerp( _hslA.s, _hslB.s, alpha ); - const l = lerp( _hslA.l, _hslB.l, alpha ); + } - this.setHSL( h, s, l ); + getUV( point, uv1, uv2, uv3, target ) { - return this; + return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } - equals( c ) { + containsPoint( point ) { - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + return Triangle.containsPoint( point, this.a, this.b, this.c ); } - fromArray( array, offset = 0 ) { - - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; + isFrontFacing( direction ) { - return this; + return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); } - toArray( array = [], offset = 0 ) { - - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; + intersectsBox( box ) { - return array; + return box.intersectsTriangle( this ); } - fromBufferAttribute( attribute, index ) { + closestPointToPoint( p, target ) { - this.r = attribute.getX( index ); - this.g = attribute.getY( index ); - this.b = attribute.getZ( index ); + const a = this.a, b = this.b, c = this.c; + let v, w; - if ( attribute.normalized === true ) { + // algorithm thanks to Real-Time Collision Detection by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., + // under the accompanying license; see chapter 5.1.5 for detailed explanation. + // basically, we're distinguishing which of the voronoi regions of the triangle + // the point lies in with the minimum amount of redundant computation. - // assuming Uint8Array + _vab.subVectors( b, a ); + _vac.subVectors( c, a ); + _vap.subVectors( p, a ); + const d1 = _vab.dot( _vap ); + const d2 = _vac.dot( _vap ); + if ( d1 <= 0 && d2 <= 0 ) { - this.r /= 255; - this.g /= 255; - this.b /= 255; + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy( a ); } - return this; - - } - - toJSON() { - - return this.getHex(); + _vbp.subVectors( p, b ); + const d3 = _vab.dot( _vbp ); + const d4 = _vac.dot( _vbp ); + if ( d3 >= 0 && d4 <= d3 ) { - } + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy( b ); - } + } - Color.NAMES = _colorKeywords; + const vc = d1 * d4 - d3 * d2; + if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - Color.prototype.isColor = true; - Color.prototype.r = 1; - Color.prototype.g = 1; - Color.prototype.b = 1; + v = d1 / ( d1 - d3 ); + // edge region of AB; barycentric coords (1-v, v, 0) + return target.copy( a ).addScaledVector( _vab, v ); - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ + } - class MeshBasicMaterial extends Material { + _vcp.subVectors( p, c ); + const d5 = _vab.dot( _vcp ); + const d6 = _vac.dot( _vcp ); + if ( d6 >= 0 && d5 <= d6 ) { - constructor( parameters ) { + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy( c ); - super(); + } - this.type = 'MeshBasicMaterial'; + const vb = d5 * d2 - d1 * d6; + if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - this.color = new Color( 0xffffff ); // emissive + w = d2 / ( d2 - d6 ); + // edge region of AC; barycentric coords (1-w, 0, w) + return target.copy( a ).addScaledVector( _vac, w ); - this.map = null; + } - this.lightMap = null; - this.lightMapIntensity = 1.0; + const va = d3 * d6 - d5 * d4; + if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + _vbc.subVectors( c, b ); + w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); + // edge region of BC; barycentric coords (0, 1-w, w) + return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - this.specularMap = null; + } - this.alphaMap = null; + // face region + const denom = 1 / ( va + vb + vc ); + // u = va * denom + v = vb * denom; + w = vc * denom; - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; + equals( triangle ) { - this.setValues( parameters ); + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } - copy( source ) { + } - super.copy( source ); + let materialId = 0; - this.color.copy( source.color ); + class Material extends EventDispatcher$1 { - this.map = source.map; + constructor() { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + super(); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + Object.defineProperty( this, 'id', { value: materialId ++ } ); - this.specularMap = source.specularMap; + this.uuid = generateUUID(); - this.alphaMap = source.alphaMap; + this.name = ''; + this.type = 'Material'; - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + this.fog = true; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + this.blending = NormalBlending; + this.side = FrontSide; + this.vertexColors = false; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + this.opacity = 1; + this.transparent = false; - return this; + 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.stencilWriteMask = 0xff; + this.stencilFunc = AlwaysStencilFunc; + this.stencilRef = 0; + this.stencilFuncMask = 0xff; + this.stencilFail = KeepStencilOp; + this.stencilZFail = KeepStencilOp; + this.stencilZPass = KeepStencilOp; + this.stencilWrite = false; - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + this.clippingPlanes = null; + this.clipIntersection = false; + this.clipShadows = false; - const _vector$9 = new Vector3(); - const _vector2 = new Vector2(); + this.shadowSide = null; - class BufferAttribute { + this.colorWrite = true; - constructor( array, itemSize, normalized ) { + this.precision = null; // override the renderer's default precision for this material - if ( Array.isArray( array ) ) { + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + this.dithering = false; - } + this.alphaToCoverage = false; + this.premultipliedAlpha = false; - this.name = ''; + this.visible = true; - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; + this.toneMapped = true; - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; + this.userData = {}; this.version = 0; - this.onUploadCallback = function () {}; + this._alphaTest = 0; } - set needsUpdate( value ) { + get alphaTest() { - if ( value === true ) this.version ++; + return this._alphaTest; } - setUsage( value ) { - - this.usage = value; - - return this; - - } + set alphaTest( value ) { - copy( source ) { + if ( this._alphaTest > 0 !== value > 0 ) { - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; + this.version ++; - this.usage = source.usage; + } - return this; + this._alphaTest = value; } - copyAt( index1, attribute, index2 ) { - - index1 *= this.itemSize; - index2 *= attribute.itemSize; + onBuild( /* shaderobject, renderer */ ) {} - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { + onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + onBeforeCompile( /* shaderobject, renderer */ ) {} - } + customProgramCacheKey() { - return this; + return this.onBeforeCompile.toString(); } - copyArray( array ) { - - this.array.set( array ); + setValues( values ) { - return this; + if ( values === undefined ) return; - } + for ( const key in values ) { - copyColorsArray( colors ) { + const newValue = values[ key ]; - const array = this.array; - let offset = 0; + if ( newValue === undefined ) { - for ( let i = 0, l = colors.length; i < l; i ++ ) { + console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); + continue; - let color = colors[ i ]; + } - if ( color === undefined ) { + // for backward compatibility if shading is set in the constructor + if ( key === 'shading' ) { - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); + console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.flatShading = ( newValue === FlatShading ) ? true : false; + continue; } - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; + const currentValue = this[ key ]; - } + if ( currentValue === undefined ) { - return this; + console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); + continue; - } + } - copyVector2sArray( vectors ) { + if ( currentValue && currentValue.isColor ) { - const array = this.array; - let offset = 0; + currentValue.set( newValue ); - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - let vector = vectors[ i ]; + currentValue.copy( newValue ); - if ( vector === undefined ) { + } else { - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); + this[ key ] = newValue; } - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - } - return this; - } - copyVector3sArray( vectors ) { - - const array = this.array; - let offset = 0; + toJSON( meta ) { - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - let vector = vectors[ i ]; + if ( isRootObject ) { - if ( vector === undefined ) { + meta = { + textures: {}, + images: {} + }; - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); + } + const data = { + metadata: { + version: 4.5, + type: 'Material', + generator: 'Material.toJSON' } + }; - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - - } + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; - return this; + if ( this.name !== '' ) data.name = this.name; - } + if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - copyVector4sArray( vectors ) { + if ( this.roughness !== undefined ) data.roughness = this.roughness; + if ( this.metalness !== undefined ) data.metalness = this.metalness; - const array = this.array; - let offset = 0; + if ( this.sheen !== undefined ) data.sheen = this.sheen; + if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); + if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; + if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); + if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); + if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; + if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.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; - let vector = vectors[ i ]; + if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - if ( vector === undefined ) { + data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); + } - } + if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; + data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; } - return this; - - } + if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - applyMatrix3( m ) { + data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; + data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - if ( this.itemSize === 2 ) { + } - for ( let i = 0, l = this.count; i < l; i ++ ) { + if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; + if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - _vector2.fromBufferAttribute( this, i ); - _vector2.applyMatrix3( m ); + if ( this.lightMap && this.lightMap.isTexture ) { - this.setXY( i, _vector2.x, _vector2.y ); + data.lightMap = this.lightMap.toJSON( meta ).uuid; + data.lightMapIntensity = this.lightMapIntensity; - } + } - } else if ( this.itemSize === 3 ) { + if ( this.aoMap && this.aoMap.isTexture ) { - for ( let i = 0, l = this.count; i < l; i ++ ) { + data.aoMap = this.aoMap.toJSON( meta ).uuid; + data.aoMapIntensity = this.aoMapIntensity; - _vector$9.fromBufferAttribute( this, i ); - _vector$9.applyMatrix3( m ); + } - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + if ( this.bumpMap && this.bumpMap.isTexture ) { - } + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; } - return this; + if ( this.normalMap && this.normalMap.isTexture ) { - } + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalMapType = this.normalMapType; + data.normalScale = this.normalScale.toArray(); - applyMatrix4( m ) { + } - for ( let i = 0, l = this.count; i < l; i ++ ) { + if ( this.displacementMap && this.displacementMap.isTexture ) { - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; - _vector$9.applyMatrix4( m ); + } - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + 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.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; + if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; - return this; + if ( this.envMap && this.envMap.isTexture ) { - } + data.envMap = this.envMap.toJSON( meta ).uuid; - applyNormalMatrix( m ) { + if ( this.combine !== undefined ) data.combine = this.combine; - for ( let i = 0, l = this.count; i < l; i ++ ) { + } - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; + if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; - _vector$9.applyNormalMatrix( m ); + if ( this.gradientMap && this.gradientMap.isTexture ) { - this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } - return this; + if ( this.transmission !== undefined ) data.transmission = this.transmission; + if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; + if ( this.thickness !== undefined ) data.thickness = this.thickness; + if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; + if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; + if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); - } + if ( this.size !== undefined ) data.size = this.size; + if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + + if ( this.blending !== NormalBlending ) data.blending = this.blending; + if ( this.side !== FrontSide ) data.side = this.side; + if ( this.vertexColors ) data.vertexColors = true; + + 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; + data.colorWrite = this.colorWrite; + + data.stencilWrite = this.stencilWrite; + data.stencilWriteMask = this.stencilWriteMask; + data.stencilFunc = this.stencilFunc; + data.stencilRef = this.stencilRef; + data.stencilFuncMask = this.stencilFuncMask; + data.stencilFail = this.stencilFail; + data.stencilZFail = this.stencilZFail; + data.stencilZPass = this.stencilZPass; + + // rotation (SpriteMaterial) + if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; + + if ( this.polygonOffset === true ) data.polygonOffset = true; + if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; + if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; + + if ( this.linewidth !== undefined && 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.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; + 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.flatShading === true ) data.flatShading = this.flatShading; + + if ( this.visible === false ) data.visible = false; + + if ( this.toneMapped === false ) data.toneMapped = false; + + if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; + + // TODO: Copied from Object3D.toJSON + + function extractFromCache( cache ) { + + const values = []; + + for ( const key in cache ) { + + const data = cache[ key ]; + delete data.metadata; + values.push( data ); + + } + + return values; + + } + + if ( isRootObject ) { + + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); + + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; + + } + + return data; + + } + + clone() { + + return new this.constructor().copy( this ); + + } + + copy( source ) { + + this.name = source.name; + + this.fog = source.fog; + + this.blending = source.blending; + this.side = source.side; + 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.stencilWriteMask = source.stencilWriteMask; + this.stencilFunc = source.stencilFunc; + this.stencilRef = source.stencilRef; + this.stencilFuncMask = source.stencilFuncMask; + this.stencilFail = source.stencilFail; + this.stencilZFail = source.stencilZFail; + this.stencilZPass = source.stencilZPass; + this.stencilWrite = source.stencilWrite; + + const srcPlanes = source.clippingPlanes; + let dstPlanes = null; + + if ( srcPlanes !== null ) { + + const n = srcPlanes.length; + dstPlanes = new Array( n ); + + for ( let i = 0; i !== n; ++ i ) { + + dstPlanes[ i ] = srcPlanes[ i ].clone(); + + } + + } + + this.clippingPlanes = dstPlanes; + this.clipIntersection = source.clipIntersection; + this.clipShadows = source.clipShadows; + + this.shadowSide = source.shadowSide; + + 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.alphaToCoverage = source.alphaToCoverage; + this.premultipliedAlpha = source.premultipliedAlpha; + + this.visible = source.visible; + + this.toneMapped = source.toneMapped; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + return this; + + } + + dispose() { + + this.dispatchEvent( { type: 'dispose' } ); + + } + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + + } + + Material.prototype.isMaterial = true; + + Material.fromType = function ( /*type*/ ) { + + // TODO: Behavior added in Materials.js + + return null; + + }; + + class MeshBasicMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.type = 'MeshBasicMaterial'; + + this.color = new Color( 0xffffff ); // emissive + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + return this; + + } + + } + + MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + + const _vector$9 = /*@__PURE__*/ new Vector3(); + const _vector2$1 = /*@__PURE__*/ new Vector2(); + + class BufferAttribute { + + constructor( array, itemSize, normalized ) { + + if ( Array.isArray( array ) ) { + + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + + } + + this.name = ''; + + this.array = array; + this.itemSize = itemSize; + this.count = array !== undefined ? array.length / itemSize : 0; + this.normalized = normalized === true; + + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + + } + + onUploadCallback() {} + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + + setUsage( value ) { + + this.usage = value; + + return this; + + } + + copy( source ) { + + this.name = source.name; + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; + + this.usage = source.usage; + + return this; + + } + + copyAt( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( let i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + } + + copyArray( array ) { + + this.array.set( array ); + + return this; + + } + + copyColorsArray( colors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = colors.length; i < l; i ++ ) { + + let color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new Color(); + + } + + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + + } + + return this; + + } + + copyVector2sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new Vector2(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + + } + + return this; + + } + + copyVector3sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new Vector3(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + + } + + return this; + + } + + copyVector4sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); + vector = new Vector4(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + array[ offset ++ ] = vector.w; + + } + + return this; + + } + + applyMatrix3( m ) { + + if ( this.itemSize === 2 ) { + + for ( let i = 0, l = this.count; i < l; i ++ ) { + + _vector2$1.fromBufferAttribute( this, i ); + _vector2$1.applyMatrix3( m ); + + this.setXY( i, _vector2$1.x, _vector2$1.y ); + + } + + } else if ( this.itemSize === 3 ) { + + for ( let i = 0, l = this.count; i < l; i ++ ) { + + _vector$9.fromBufferAttribute( this, i ); + _vector$9.applyMatrix3( m ); + + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + + } + + } + + return this; + + } + + applyMatrix4( m ) { + + for ( let i = 0, l = this.count; i < l; i ++ ) { + + _vector$9.fromBufferAttribute( this, i ); + + _vector$9.applyMatrix4( m ); + + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + + } + + return this; + + } + + applyNormalMatrix( m ) { + + for ( let i = 0, l = this.count; i < l; i ++ ) { + + _vector$9.fromBufferAttribute( this, i ); + + _vector$9.applyNormalMatrix( m ); + + this.setXYZ( i, _vector$9.x, _vector$9.y, _vector$9.z ); + + } + + return this; + + } transformDirection( m ) { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + _vector$9.fromBufferAttribute( this, i ); _vector$9.transformDirection( m ); @@ -10967,30 +11144,14 @@ } - function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; - - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; - - } - - return max; - - } - - let _id = 0; + let _id$1 = 0; - const _m1 = new Matrix4(); - const _obj = new Object3D(); - const _offset = new Vector3(); - const _box$1 = new Box3(); - const _boxMorphTargets = new Box3(); - const _vector$8 = new Vector3(); + const _m1 = /*@__PURE__*/ new Matrix4(); + const _obj = /*@__PURE__*/ new Object3D(); + const _offset = /*@__PURE__*/ new Vector3(); + const _box$1 = /*@__PURE__*/ new Box3(); + const _boxMorphTargets = /*@__PURE__*/ new Box3(); + const _vector$8 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher$1 { @@ -10998,7 +11159,7 @@ super(); - Object.defineProperty( this, 'id', { value: _id ++ } ); + Object.defineProperty( this, 'id', { value: _id$1 ++ } ); this.uuid = generateUUID(); @@ -11032,7 +11193,7 @@ if ( Array.isArray( index ) ) { - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); + this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { @@ -11147,11 +11308,21 @@ } - rotateX( angle ) { + applyQuaternion( q ) { - // rotate geometry around world x-axis + _m1.makeRotationFromQuaternion( q ); - _m1.makeRotationX( angle ); + this.applyMatrix4( _m1 ); + + return this; + + } + + rotateX( angle ) { + + // rotate geometry around world x-axis + + _m1.makeRotationX( angle ); this.applyMatrix4( _m1 ); @@ -11430,12 +11601,6 @@ } - computeFaceNormals() { - - // backwards compatibility - - } - computeTangents() { const index = this.index; @@ -11461,13 +11626,13 @@ const nVertices = positions.length / 3; - if ( attributes.tangent === undefined ) { + if ( this.hasAttribute( 'tangent' ) === false ) { this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } - const tangents = attributes.tangent.array; + const tangents = this.getAttribute( 'tangent' ).array; const tan1 = [], tan2 = []; @@ -11770,7 +11935,15 @@ for ( let i = 0, l = indices.length; i < l; i ++ ) { - index = indices[ i ] * itemSize; + if ( attribute.isInterleavedBufferAttribute ) { + + index = indices[ i ] * attribute.data.stride + attribute.offset; + + } else { + + index = indices[ i ] * itemSize; + + } for ( let j = 0; j < itemSize; j ++ ) { @@ -11965,31 +12138,7 @@ clone() { - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); } @@ -12094,6 +12243,10 @@ this.userData = source.userData; + // geometry generator parameters + + if ( source.parameters !== undefined ) this.parameters = Object.assign( {}, source.parameters ); + return this; } @@ -12269,7 +12422,7 @@ const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -12327,7 +12480,7 @@ const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -12426,7 +12579,7 @@ const morphInfluences = object.morphTargetInfluences; - if ( material.morphTargets && morphPosition && morphInfluences ) { + if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); _morphB.set( 0, 0, 0 ); @@ -12465,7 +12618,7 @@ } - if ( object.isSkinnedMesh && material.skinning ) { + if ( object.isSkinnedMesh ) { object.boneTransform( a, _vA$1 ); object.boneTransform( b, _vB$1 ); @@ -12672,6 +12825,12 @@ } + static fromJSON( data ) { + + return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); + + } + } /** @@ -12743,25 +12902,6 @@ var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - /** - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - class ShaderMaterial extends Material { constructor( parameters ) { @@ -12785,10 +12925,6 @@ 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 @@ -12840,11 +12976,6 @@ this.lights = source.lights; this.clipping = source.clipping; - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; @@ -12979,13 +13110,6 @@ getWorldDirection( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; @@ -13314,9 +13438,14 @@ const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; - const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); + const currentOutputEncoding = renderer.outputEncoding; + const currentToneMapping = renderer.toneMapping; + const currentXrEnabled = renderer.xr.enabled; + + renderer.outputEncoding = LinearEncoding; + renderer.toneMapping = NoToneMapping; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; @@ -13345,8 +13474,12 @@ renderer.setRenderTarget( currentRenderTarget ); + renderer.outputEncoding = currentOutputEncoding; + renderer.toneMapping = currentToneMapping; renderer.xr.enabled = currentXrEnabled; + renderTarget.texture.needsPMREMUpdate = true; + } } @@ -13357,22 +13490,9 @@ images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - // Why CubeTexture._needsFlipEnvMap is necessary: - // - // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) - // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, - // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - - // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped - // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false) - // when using WebGLCubeRenderTarget.texture as a cube texture. - - this._needsFlipEnvMap = true; - this.flipY = false; } @@ -13395,33 +13515,33 @@ class WebGLCubeRenderTarget extends WebGLRenderTarget { - constructor( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { + constructor( size, options = {} ) { - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); + super( size, size, options ); - options = dummy; + const image = { width: size, height: size, depth: 1 }; + const images = [ image, image, image, image, image, image ]; - } + this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - super( size, size, options ); + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - options = options || {}; + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture + // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). - this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - this.texture._needsFlipEnvMap = false; - } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; - this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; @@ -13529,28 +13649,206 @@ WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; - class DataTexture extends Texture { + const _vector1 = /*@__PURE__*/ new Vector3(); + const _vector2 = /*@__PURE__*/ new Vector3(); + const _normalMatrix = /*@__PURE__*/ new Matrix3(); - constructor( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + class Plane { - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { - this.image = { data: data || null, width: width || 1, height: height || 1 }; + // normal is assumed to be normalized - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + this.normal = normal; + this.constant = constant; - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + } - this.needsUpdate = true; + set( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + } + + setComponents( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + } + + setFromNormalAndCoplanarPoint( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); + + return this; + + } + + setFromCoplanarPoints( a, b, c ) { + + const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + } + + copy( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + } + + normalize() { + + // Note: will lead to a divide by zero if the plane is invalid. + + const inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + } + + negate() { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + } + + distanceToPoint( point ) { + + return this.normal.dot( point ) + this.constant; + + } + + distanceToSphere( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + } + + projectPoint( point, target ) { + + return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + + } + + intersectLine( line, target ) { + + const direction = line.delta( _vector1 ); + + const denominator = this.normal.dot( direction ); + + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { + + return target.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return null; + + } + + const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return null; + + } + + return target.copy( direction ).multiplyScalar( t ).add( line.start ); + + } + + intersectsLine( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + const startSign = this.distanceToPoint( line.start ); + const endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + } + + intersectsBox( box ) { + + return box.intersectsPlane( this ); + + } + + intersectsSphere( sphere ) { + + return sphere.intersectsPlane( this ); + + } + + coplanarPoint( target ) { + + return target.copy( this.normal ).multiplyScalar( - this.constant ); + + } + + applyMatrix4( matrix, optionalNormalMatrix ) { + + const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); + + const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); + + const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + + this.constant = - referencePoint.dot( normal ); + + return this; + + } + + translate( offset ) { + + this.constant -= offset.dot( this.normal ); + + return this; + + } + + equals( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + + } + + clone() { + + return new this.constructor().copy( this ); } } - DataTexture.prototype.isDataTexture = true; + Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/ new Sphere(); const _vector$7 = /*@__PURE__*/ new Vector3(); @@ -13778,16 +14076,12 @@ attribute.onUploadCallback(); - let type = 5126; + let type; if ( array instanceof Float32Array ) { type = 5126; - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { @@ -13798,7 +14092,7 @@ } else { - console.warn( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); + throw new Error( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); } @@ -13828,6 +14122,14 @@ type = 5121; + } else if ( array instanceof Uint8ClampedArray ) { + + type = 5121; + + } else { + + throw new Error( 'THREE.WebGLAttributes: Unsupported buffer data format: ' + array ); + } return { @@ -14022,15 +14324,23 @@ } + static fromJSON( data ) { + + return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); + + } + } var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; + var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; - 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( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; + var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; + + 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( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; @@ -14038,7 +14348,7 @@ var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\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\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\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}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\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 vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( 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 vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\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#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; + var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat V_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_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( V * 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}\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_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif"; 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, float faceDirection ) {\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 ) * faceDirection;\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"; @@ -14058,9 +14368,9 @@ var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(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}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\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#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\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}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; + var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( 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 max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\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}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\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#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\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}\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}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; + var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\t\t#ifdef texture2DGradEXT\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\n\t\t#else\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\t\t#endif\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, CUBEUV_MAX_MIP );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; @@ -14068,17 +14378,17 @@ var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; + var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - 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.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\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.a );\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.a );\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.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, 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.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.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 = cLogLuvM * value.rgb;\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 = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; + var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) {\n\treturn value;\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.a );\n}"; - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, 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_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\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"; + var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, 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_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\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"; - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; + var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; @@ -14086,41 +14396,41 @@ var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\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"; - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; + var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; + var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\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"; + var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - 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"; + var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\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"; - var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; + var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn vec3( texture2D( gradientMap, coord ).r );\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; + var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; 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 = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; + var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointLightInfo( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotLightInfo( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalLightInfo( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; - var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\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};\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};\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};\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"; + var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#else\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.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};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.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};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.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 vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\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\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 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 roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, 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\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; + var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n#endif"; var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; + var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; + var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, 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_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)"; - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; + var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\t#ifdef SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULARINTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a;\n\t\t#endif\n\t\t#ifdef USE_SPECULARCOLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vUv ).rgb;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a;\n\t#endif\n#endif"; - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#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#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\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 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\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\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}"; + var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec3 sheenSpecular = vec3( 0.0 );\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\treturn saturate( DG * RECIPROCAL_PI );\n}\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, 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 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\tvec3 FssEss = specularColor * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\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.roughness;\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#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\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_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tcomputeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\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\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}"; - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; + var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef USE_CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry.normal );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * 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 defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; + var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometry.normal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; @@ -14132,11 +14442,11 @@ var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; + var map_fragment = "#ifdef USE_MAP\n\tvec4 sampledDiffuseColor = texture2D( map, vUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\tsampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );\n\t#endif\n\tdiffuseColor *= sampledDiffuseColor;\n#endif"; var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; + var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tdiffuseColor *= texture2D( map, uv );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; @@ -14144,25 +14454,35 @@ var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; + var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\n\tvColor *= morphTargetBaseInfluence;\n\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t#if defined( USE_COLOR_ALPHA )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\n\t\t#elif defined( USE_COLOR )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\n\t\t#endif\n\t}\n#endif"; + + var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\t#endif\n#endif"; - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; + var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform ivec2 morphTargetsTextureSize;\n\t\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\n\t\t\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\n\t\t\tint y = texelIndex / morphTargetsTextureSize.x;\n\t\t\tint x = texelIndex - y * morphTargetsTextureSize.x;\n\t\t\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\n\t\t\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\n\t\t}\n\t#else\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\t\t#else\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\t\t#endif\n\t#endif\n#endif"; - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; + var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t\t#endif\n\t#endif\n#endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#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 * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; + var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; + + var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; + + var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; + + var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\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 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif"; - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; + var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; - 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}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\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}"; + var output_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= transmissionAlpha + 0.1;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; + + 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}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\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}"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; @@ -14198,11 +14518,11 @@ var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\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}\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}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; + var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\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}\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}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; + var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tfloat transmissionAlpha = 1.0;\n\tfloat transmissionFactor = transmission;\n\tfloat thicknessFactor = thickness;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\ttransmissionFactor *= texture2D( transmissionMap, vUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tthicknessFactor *= texture2D( thicknessMap, vUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmission = getIBLVolumeRefraction(\n\t\tn, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor,\n\t\tattenuationColor, attenuationDistance );\n\ttotalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor );\n\ttransmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor );\n#endif"; - var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; + var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\t\tfloat framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\t#ifdef texture2DLodEXT\n\t\t\treturn texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#else\n\t\t\treturn texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#endif\n\t}\n\tvec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tif ( attenuationDistance == 0.0 ) {\n\t\t\treturn radiance;\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance * radiance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a );\n\t}\n#endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; @@ -14216,76 +14536,77 @@ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; + var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + const vertex$g = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; + const fragment$g = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tgl_FragColor = texture2D( t2D, vUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\tgl_FragColor = vec4( mix( pow( gl_FragColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), gl_FragColor.rgb * 0.0773993808, vec3( lessThanEqual( gl_FragColor.rgb, vec3( 0.04045 ) ) ) ), gl_FragColor.w );\n\t#endif\n\t#include \n\t#include \n}"; - var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; + const vertex$f = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; + const fragment$f = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; + const vertex$e = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; + const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; + const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; + const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + const vertex$c = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; + const fragment$c = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\t#include \n\t#include \n}"; - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$b = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$b = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$a = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \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 \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$9 = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$9 = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_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_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; + const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t#else\n\t\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULARINTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n\t#ifdef USE_SPECULARCOLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; + const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; + const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const vertex$3 = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; + const vertex$2 = "#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + const fragment$2 = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; + const vertex$1 = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; + const fragment$1 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, + alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, @@ -14343,15 +14664,20 @@ map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, + morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, + normal_pars_fragment: normal_pars_fragment, + normal_pars_vertex: normal_pars_vertex, + normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, + output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, @@ -14371,8 +14697,8 @@ specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, - transmissionmap_fragment: transmissionmap_fragment, - transmissionmap_pars_fragment: transmissionmap_pars_fragment, + transmission_fragment: transmission_fragment, + transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, @@ -14381,38 +14707,38 @@ uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, - background_frag: background_frag, - background_vert: background_vert, - 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, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_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, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert + background_vert: vertex$g, + background_frag: fragment$g, + cube_vert: vertex$f, + cube_frag: fragment$f, + depth_vert: vertex$e, + depth_frag: fragment$e, + distanceRGBA_vert: vertex$d, + distanceRGBA_frag: fragment$d, + equirect_vert: vertex$c, + equirect_frag: fragment$c, + linedashed_vert: vertex$b, + linedashed_frag: fragment$b, + meshbasic_vert: vertex$a, + meshbasic_frag: fragment$a, + meshlambert_vert: vertex$9, + meshlambert_frag: fragment$9, + meshmatcap_vert: vertex$8, + meshmatcap_frag: fragment$8, + meshnormal_vert: vertex$7, + meshnormal_frag: fragment$7, + meshphong_vert: vertex$6, + meshphong_frag: fragment$6, + meshphysical_vert: vertex$5, + meshphysical_frag: fragment$5, + meshtoon_vert: vertex$4, + meshtoon_frag: fragment$4, + points_vert: vertex$3, + points_frag: fragment$3, + shadow_vert: vertex$2, + shadow_frag: fragment$2, + sprite_vert: vertex$1, + sprite_frag: fragment$1 }; /** @@ -14423,7 +14749,7 @@ common: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, @@ -14431,6 +14757,7 @@ uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, + alphaTest: { value: 0 } }, @@ -14444,9 +14771,9 @@ envMap: { value: null }, flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } + reflectivity: { value: 1.0 }, // basic, lambert, phong + ior: { value: 1.5 }, // physical + refractionRatio: { value: 0.98 } // basic, lambert, phong }, @@ -14600,24 +14927,26 @@ points: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } @@ -14811,8 +15140,8 @@ } ] ), - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag + vertexShader: ShaderChunk.meshnormal_vert, + fragmentShader: ShaderChunk.meshnormal_frag }, @@ -14914,9 +15243,23 @@ clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, + sheen: { value: 0 }, + sheenColor: { value: new Color( 0x000000 ) }, + sheenColorMap: { value: null }, + sheenRoughness: { value: 1 }, + sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, + transmissionSamplerSize: { value: new Vector2() }, + transmissionSamplerMap: { value: null }, + thickness: { value: 0 }, + thicknessMap: { value: null }, + attenuationDistance: { value: 0 }, + attenuationColor: { value: new Color( 0x000000 ) }, + specularIntensity: { value: 1 }, + specularIntensityMap: { value: null }, + specularColor: { value: new Color( 1, 1, 1 ) }, + specularColorMap: { value: null }, } ] ), @@ -14925,10 +15268,10 @@ }; - function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) { + function WebGLBackground( renderer, cubemaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); - let clearAlpha = 0; + let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; @@ -14937,8 +15280,9 @@ let currentBackgroundVersion = 0; let currentTonemapping = null; - function render( renderList, scene, camera, forceClear ) { + function render( renderList, scene ) { + let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { @@ -15019,7 +15363,7 @@ } boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background._needsFlipEnvMap ) ? - 1 : 1; + boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; if ( currentBackground !== background || currentBackgroundVersion !== background.version || @@ -15149,6 +15493,7 @@ const defaultState = createBindingState( null ); let currentState = defaultState; + let forceUpdate = false; function setup( object, material, program, geometry, index ) { @@ -15199,7 +15544,9 @@ } - if ( updateBuffers ) { + if ( updateBuffers || forceUpdate ) { + + forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); @@ -15462,9 +15809,16 @@ const programAttribute = programAttributes[ name ]; - if ( programAttribute >= 0 ) { + if ( programAttribute.location >= 0 ) { + + let geometryAttribute = geometryAttributes[ name ]; - const geometryAttribute = geometryAttributes[ name ]; + if ( geometryAttribute === undefined ) { + + if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; + + } if ( geometryAttribute !== undefined ) { @@ -15487,87 +15841,87 @@ const stride = data.stride; const offset = geometryAttribute.offset; - if ( data && data.isInstancedInterleavedBuffer ) { - - enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); + if ( data.isInstancedInterleavedBuffer ) { - if ( geometry._maxInstanceCount === undefined ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - geometry._maxInstanceCount = data.meshPerAttribute * data.count; + enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } - } else { - - enableAttribute( programAttribute ); + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - } + geometry._maxInstanceCount = data.meshPerAttribute * data.count; - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); + } - } else { + } else { - if ( geometryAttribute.isInstancedBufferAttribute ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); + enableAttribute( programAttribute.location + i ); - if ( geometry._maxInstanceCount === undefined ) { + } - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + } - } + gl.bindBuffer( 34962, buffer ); - } else { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttribute( programAttribute ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + stride * bytesPerElement, + ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement + ); } - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); + } else { - } + if ( geometryAttribute.isInstancedBufferAttribute ) { - } else if ( name === 'instanceMatrix' ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - const attribute = attributes.get( object.instanceMatrix ); + enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - const buffer = attribute.buffer; - const type = attribute.type; + geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - enableAttributeAndDivisor( programAttribute + 0, 1 ); - enableAttributeAndDivisor( programAttribute + 1, 1 ); - enableAttributeAndDivisor( programAttribute + 2, 1 ); - enableAttributeAndDivisor( programAttribute + 3, 1 ); + } - gl.bindBuffer( 34962, buffer ); + } else { - gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - } else if ( name === 'instanceColor' ) { + enableAttribute( programAttribute.location + i ); - const attribute = attributes.get( object.instanceColor ); + } - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + gl.bindBuffer( 34962, buffer ); - const buffer = attribute.buffer; - const type = attribute.type; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, 1 ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + size * bytesPerElement, + ( size / programAttribute.locationSize ) * i * bytesPerElement + ); - gl.bindBuffer( 34962, buffer ); + } - gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 ); + } } else if ( materialDefaultAttributeValues !== undefined ) { @@ -15578,19 +15932,19 @@ switch ( value.length ) { case 2: - gl.vertexAttrib2fv( programAttribute, value ); + gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: - gl.vertexAttrib3fv( programAttribute, value ); + gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: - gl.vertexAttrib4fv( programAttribute, value ); + gl.vertexAttrib4fv( programAttribute.location, value ); break; default: - gl.vertexAttrib1fv( programAttribute, value ); + gl.vertexAttrib1fv( programAttribute.location, value ); } @@ -15689,6 +16043,7 @@ function reset() { resetDefaultState(); + forceUpdate = true; if ( currentState === defaultState ) return; @@ -15697,7 +16052,7 @@ } - // for backward-compatilibity + // for backward-compatibility function resetDefaultState() { @@ -15837,10 +16192,8 @@ } - /* eslint-disable no-undef */ const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; const maxPrecision = getMaxPrecision( precision ); @@ -15852,6 +16205,8 @@ } + const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ); + const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( 34930 ); @@ -15874,6 +16229,8 @@ isWebGL2: isWebGL2, + drawBuffers: drawBuffers, + getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, @@ -16084,7 +16441,7 @@ function get( texture ) { - if ( texture && texture.isTexture ) { + if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; @@ -16101,14 +16458,10 @@ if ( image && image.height > 0 ) { - const currentRenderTarget = renderer.getRenderTarget(); - const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); - renderer.setRenderTarget( currentRenderTarget ); - texture.addEventListener( 'dispose', onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); @@ -16161,14941 +16514,14684 @@ } - function WebGLExtensions( gl ) { + class OrthographicCamera extends Camera { - const extensions = {}; + constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { - function getExtension( name ) { + super(); - if ( extensions[ name ] !== undefined ) { + this.type = 'OrthographicCamera'; - return extensions[ name ]; + this.zoom = 1; + this.view = null; - } + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - let extension; + this.near = near; + this.far = far; - switch ( name ) { + this.updateProjectionMatrix(); - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; + } - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; + copy( source, recursive ) { - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; + super.copy( source, recursive ); - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - default: - extension = gl.getExtension( name ); + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); + + return this; + + } + + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { + + if ( this.view === null ) { + + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; } - extensions[ name ] = extension; + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; - return extension; + this.updateProjectionMatrix(); } - return { + clearViewOffset() { - has: function ( name ) { + if ( this.view !== null ) { - return getExtension( name ) !== null; + this.view.enabled = false; - }, + } - init: function ( capabilities ) { + this.updateProjectionMatrix(); - if ( capabilities.isWebGL2 ) { + } - getExtension( 'EXT_color_buffer_float' ); + updateProjectionMatrix() { - } else { + const dx = ( this.right - this.left ) / ( 2 * this.zoom ); + const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + const cx = ( this.right + this.left ) / 2; + const cy = ( this.top + this.bottom ) / 2; - getExtension( 'WEBGL_depth_texture' ); - getExtension( 'OES_texture_float' ); - getExtension( 'OES_texture_half_float' ); - getExtension( 'OES_texture_half_float_linear' ); - getExtension( 'OES_standard_derivatives' ); - getExtension( 'OES_element_index_uint' ); - getExtension( 'OES_vertex_array_object' ); - getExtension( 'ANGLE_instanced_arrays' ); + let left = cx - dx; + let right = cx + dx; + let top = cy + dy; + let bottom = cy - dy; - } + if ( this.view !== null && this.view.enabled ) { - getExtension( 'OES_texture_float_linear' ); - getExtension( 'EXT_color_buffer_half_float' ); + const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; + const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - }, + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; - get: function ( name ) { + } - const extension = getExtension( name ); + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - if ( extension === null ) { + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + } - } + toJSON( meta ) { - return extension; + const data = super.toJSON( meta ); - } + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; - }; + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - } + return data; - function WebGLGeometries( gl, attributes, info, bindingStates ) { + } - const geometries = {}; - const wireframeAttributes = new WeakMap(); + } - function onGeometryDispose( event ) { + OrthographicCamera.prototype.isOrthographicCamera = true; - const geometry = event.target; + const LOD_MIN = 4; + + // The standard deviations (radians) associated with the extra mips. These are + // chosen to approximate a Trowbridge-Reitz distribution function times the + // geometric shadowing function. These sigma values squared must match the + // variance #defines in cube_uv_reflection_fragment.glsl.js. + const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; + + // The maximum length of the blur for loop. Smaller sigmas will use fewer + // samples and exit early, but not recompile the shader. + const MAX_SAMPLES = 20; + + const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); + const _clearColor = /*@__PURE__*/ new Color(); + let _oldTarget = null; + + // Golden Ratio + const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; + const INV_PHI = 1 / PHI; + + // Vertices of a dodecahedron (except the opposites, which represent the + // same axis), used as axis directions evenly spread on a sphere. + const _axisDirections = [ + /*@__PURE__*/ new Vector3( 1, 1, 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, 1 ), + /*@__PURE__*/ new Vector3( 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), + /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), + /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), + /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; - if ( geometry.index !== null ) { + /** + * This class generates a Prefiltered, Mipmapped Radiance Environment Map + * (PMREM) from a cubeMap environment texture. This allows different levels of + * blur to be quickly accessed based on material roughness. It is packed into a + * special CubeUV format that allows us to perform custom interpolation so that + * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap + * chain, it only goes down to the LOD_MIN level (above), and then creates extra + * even more filtered 'mips' at the same LOD_MIN resolution, associated with + * higher roughness levels. In this way we maintain resolution to smoothly + * interpolate diffuse lighting while limiting sampling computation. + * + * Paper: Fast, Accurate Image-Based Lighting + * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view + */ - attributes.remove( geometry.index ); + class PMREMGenerator { - } + constructor( renderer ) { - for ( const name in geometry.attributes ) { + this._renderer = renderer; + this._pingPongRenderTarget = null; - attributes.remove( geometry.attributes[ name ] ); + this._lodMax = 0; + this._cubeSize = 0; + this._lodPlanes = []; + this._sizeLods = []; + this._sigmas = []; - } + this._blurMaterial = null; + this._cubemapMaterial = null; + this._equirectMaterial = null; - geometry.removeEventListener( 'dispose', onGeometryDispose ); + this._compileMaterial( this._blurMaterial ); - delete geometries[ geometry.id ]; + } - const attribute = wireframeAttributes.get( geometry ); + /** + * Generates a PMREM from a supplied Scene, which can be faster than using an + * image if networking bandwidth is low. Optional sigma specifies a blur radius + * in radians to be applied to the scene before PMREM generation. Optional near + * and far planes ensure the scene is rendered in its entirety (the cubeCamera + * is placed at the origin). + */ + fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { - if ( attribute ) { + _oldTarget = this._renderer.getRenderTarget(); - attributes.remove( attribute ); - wireframeAttributes.delete( geometry ); + this._setSize( 256 ); - } + const cubeUVRenderTarget = this._allocateTargets(); + cubeUVRenderTarget.depthBuffer = true; - bindingStates.releaseStatesOfGeometry( geometry ); + this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); - if ( geometry.isInstancedBufferGeometry === true ) { + if ( sigma > 0 ) { - delete geometry._maxInstanceCount; + this._blur( cubeUVRenderTarget, 0, 0, sigma ); } - // + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - info.memory.geometries --; + return cubeUVRenderTarget; } - function get( object, geometry ) { - - if ( geometries[ geometry.id ] === true ) return geometry; + /** + * Generates a PMREM from an equirectangular texture, which can be either LDR + * or HDR. The ideal input image size is 1k (1024 x 512), + * as this matches best with the 256 x 256 cubemap output. + */ + fromEquirectangular( equirectangular, renderTarget = null ) { - geometry.addEventListener( 'dispose', onGeometryDispose ); + return this._fromTexture( equirectangular, renderTarget ); - geometries[ geometry.id ] = true; + } - info.memory.geometries ++; + /** + * Generates a PMREM from an cubemap texture, which can be either LDR + * or HDR. The ideal input cube size is 256 x 256, + * as this matches best with the 256 x 256 cubemap output. + */ + fromCubemap( cubemap, renderTarget = null ) { - return geometry; + return this._fromTexture( cubemap, renderTarget ); } - function update( geometry ) { - - const geometryAttributes = geometry.attributes; - - // Updating index buffer in VAO now. See WebGLBindingStates. + /** + * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileCubemapShader() { - for ( const name in geometryAttributes ) { + if ( this._cubemapMaterial === null ) { - attributes.update( geometryAttributes[ name ], 34962 ); + this._cubemapMaterial = _getCubemapMaterial(); + this._compileMaterial( this._cubemapMaterial ); } - // morph targets + } - const morphAttributes = geometry.morphAttributes; + /** + * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileEquirectangularShader() { - for ( const name in morphAttributes ) { + if ( this._equirectMaterial === null ) { - const array = morphAttributes[ name ]; + this._equirectMaterial = _getEquirectMaterial(); + this._compileMaterial( this._equirectMaterial ); - for ( let i = 0, l = array.length; i < l; i ++ ) { + } - attributes.update( array[ i ], 34962 ); + } - } + /** + * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, + * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on + * one of them will cause any others to also become unusable. + */ + dispose() { - } + this._dispose(); + + if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); + if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } - function updateWireframeAttribute( geometry ) { + // private interface - const indices = []; + _setSize( cubeSize ) { - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; + this._lodMax = Math.floor( Math.log2( cubeSize ) ); + this._cubeSize = Math.pow( 2, this._lodMax ); - if ( geometryIndex !== null ) { + } - const array = geometryIndex.array; - version = geometryIndex.version; + _dispose() { - for ( let i = 0, l = array.length; i < l; i += 3 ) { + if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; + if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); - indices.push( a, b, b, c, c, a ); + for ( let i = 0; i < this._lodPlanes.length; i ++ ) { - } + this._lodPlanes[ i ].dispose(); - } else { + } - const array = geometryPosition.array; - version = geometryPosition.version; + } - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + _cleanup( outputTarget ) { - const a = i + 0; - const b = i + 1; - const c = i + 2; + this._renderer.setRenderTarget( _oldTarget ); + outputTarget.scissorTest = false; + _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); - indices.push( a, b, b, c, c, a ); + } - } + _fromTexture( texture, renderTarget ) { - } + if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; + this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); - // Updating index buffer in VAO now. See WebGLBindingStates + } else { // Equirectangular - // + this._setSize( texture.image.width / 4 ); - const previousAttribute = wireframeAttributes.get( geometry ); + } - if ( previousAttribute ) attributes.remove( previousAttribute ); + _oldTarget = this._renderer.getRenderTarget(); - // + const cubeUVRenderTarget = renderTarget || this._allocateTargets(); + this._textureToCubeUV( texture, cubeUVRenderTarget ); + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - wireframeAttributes.set( geometry, attribute ); + return cubeUVRenderTarget; } - function getWireframeAttribute( geometry ) { - - const currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { + _allocateTargets() { - const geometryIndex = geometry.index; + const width = 3 * Math.max( this._cubeSize, 16 * 7 ); + const height = 4 * this._cubeSize - 32; - if ( geometryIndex !== null ) { + const params = { + magFilter: LinearFilter, + minFilter: LinearFilter, + generateMipmaps: false, + type: HalfFloatType, + format: RGBAFormat, + encoding: LinearEncoding, + depthBuffer: false + }; - // if the attribute is obsolete, create a new one + const cubeUVRenderTarget = _createRenderTarget( width, height, params ); - if ( currentAttribute.version < geometryIndex.version ) { + if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width ) { - updateWireframeAttribute( geometry ); + if ( this._pingPongRenderTarget !== null ) { - } + this._dispose(); } - } else { + this._pingPongRenderTarget = _createRenderTarget( width, height, params ); - updateWireframeAttribute( geometry ); + const { _lodMax } = this; + ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); + + this._blurMaterial = _getBlurShader( _lodMax, width, height ); } - return wireframeAttributes.get( geometry ); + return cubeUVRenderTarget; } - return { + _compileMaterial( material ) { - get: get, - update: update, + const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); + this._renderer.compile( tmpMesh, _flatCamera ); - getWireframeAttribute: getWireframeAttribute + } - }; + _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { - } + const fov = 90; + const aspect = 1; + const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); + const upSign = [ 1, - 1, 1, 1, 1, 1 ]; + const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; + const renderer = this._renderer; - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { + const originalAutoClear = renderer.autoClear; + const toneMapping = renderer.toneMapping; + renderer.getClearColor( _clearColor ); - const isWebGL2 = capabilities.isWebGL2; + renderer.toneMapping = NoToneMapping; + renderer.autoClear = false; - let mode; + const backgroundMaterial = new MeshBasicMaterial( { + name: 'PMREM.Background', + side: BackSide, + depthWrite: false, + depthTest: false, + } ); - function setMode( value ) { + const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); - mode = value; + let useSolidColor = false; + const background = scene.background; - } + if ( background ) { - let type, bytesPerElement; + if ( background.isColor ) { - function setIndex( value ) { + backgroundMaterial.color.copy( background ); + scene.background = null; + useSolidColor = true; - type = value.type; - bytesPerElement = value.bytesPerElement; + } - } + } else { - function render( start, count ) { + backgroundMaterial.color.copy( _clearColor ); + useSolidColor = true; - gl.drawElements( mode, count, type, start * bytesPerElement ); + } - info.update( count, mode, 1 ); + for ( let i = 0; i < 6; i ++ ) { - } + const col = i % 3; - function renderInstances( start, count, primcount ) { + if ( col === 0 ) { - if ( primcount === 0 ) return; + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - let extension, methodName; + } else if ( col === 1 ) { - if ( isWebGL2 ) { + cubeCamera.up.set( 0, 0, upSign[ i ] ); + cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - extension = gl; - methodName = 'drawElementsInstanced'; + } else { - } else { + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; + } - if ( extension === null ) { + const size = this._cubeSize; - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); + + renderer.setRenderTarget( cubeUVRenderTarget ); + + if ( useSolidColor ) { + + renderer.render( backgroundBox, cubeCamera ); } + renderer.render( scene, cubeCamera ); + } - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); + backgroundBox.geometry.dispose(); + backgroundBox.material.dispose(); - info.update( count, mode, primcount ); + renderer.toneMapping = toneMapping; + renderer.autoClear = originalAutoClear; + scene.background = background; } - // + _textureToCubeUV( texture, cubeUVRenderTarget ) { - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; + const renderer = this._renderer; - } + const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); - function WebGLInfo( gl ) { + if ( isCubeTexture ) { - const memory = { - geometries: 0, - textures: 0 - }; + if ( this._cubemapMaterial === null ) { - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; + this._cubemapMaterial = _getCubemapMaterial(); - function update( count, mode, instanceCount ) { + } - render.calls ++; + this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; - switch ( mode ) { + } else { - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; + if ( this._equirectMaterial === null ) { - case 1: - render.lines += instanceCount * ( count / 2 ); - break; + this._equirectMaterial = _getEquirectMaterial(); - case 3: - render.lines += instanceCount * ( count - 1 ); - break; + } - case 2: - render.lines += instanceCount * count; - break; + } - case 0: - render.points += instanceCount * count; - break; + const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; + const mesh = new Mesh( this._lodPlanes[ 0 ], material ); - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; + const uniforms = material.uniforms; - } + uniforms[ 'envMap' ].value = texture; - } + const size = this._cubeSize; - function reset() { + _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; + renderer.setRenderTarget( cubeUVRenderTarget ); + renderer.render( mesh, _flatCamera ); } - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; + _applyPMREM( cubeUVRenderTarget ) { - } + const renderer = this._renderer; + const autoClear = renderer.autoClear; + renderer.autoClear = false; - function numericalSort( a, b ) { + for ( let i = 1; i < this._lodPlanes.length; i ++ ) { - return a[ 0 ] - b[ 0 ]; + const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); - } + const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; - function absNumericalSort( a, b ) { + this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); + } - } + renderer.autoClear = autoClear; - function WebGLMorphtargets( gl ) { + } - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); + /** + * This is a two-pass Gaussian blur for a cubemap. Normally this is done + * vertically and horizontally, but this breaks down on a cube. Here we apply + * the blur latitudinally (around the poles), and then longitudinally (towards + * the poles) to approximate the orthogonally-separable blur. It is least + * accurate at the poles, but still does a decent job. + */ + _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { - const workInfluences = []; + const pingPongRenderTarget = this._pingPongRenderTarget; - for ( let i = 0; i < 8; i ++ ) { + this._halfBlur( + cubeUVRenderTarget, + pingPongRenderTarget, + lodIn, + lodOut, + sigma, + 'latitudinal', + poleAxis ); - workInfluences[ i ] = [ i, 0 ]; + this._halfBlur( + pingPongRenderTarget, + cubeUVRenderTarget, + lodOut, + lodOut, + sigma, + 'longitudinal', + poleAxis ); } - function update( object, geometry, material, program ) { + _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - const objectInfluences = object.morphTargetInfluences; + const renderer = this._renderer; + const blurMaterial = this._blurMaterial; - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences + if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - const length = objectInfluences === undefined ? 0 : objectInfluences.length; + console.error( + 'blur direction must be either latitudinal or longitudinal!' ); - let influences = influencesList[ geometry.id ]; + } - if ( influences === undefined ) { + // Number of standard deviations at which to cut off the discrete approximation. + const STANDARD_DEVIATIONS = 3; - // initialise list + const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); + const blurUniforms = blurMaterial.uniforms; - influences = []; + const pixels = this._sizeLods[ lodIn ] - 1; + const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); + const sigmaPixels = sigmaRadians / radiansPerPixel; + const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; - for ( let i = 0; i < length; i ++ ) { + if ( samples > MAX_SAMPLES ) { - influences[ i ] = [ i, 0 ]; + console.warn( `sigmaRadians, ${ + sigmaRadians}, is too large and will clip, as it requested ${ + samples} samples when the maximum is set to ${MAX_SAMPLES}` ); - } + } - influencesList[ geometry.id ] = influences; + const weights = []; + let sum = 0; - } + for ( let i = 0; i < MAX_SAMPLES; ++ i ) { - // Collect influences + const x = i / sigmaPixels; + const weight = Math.exp( - x * x / 2 ); + weights.push( weight ); - for ( let i = 0; i < length; i ++ ) { + if ( i === 0 ) { - const influence = influences[ i ]; + sum += weight; - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; + } else if ( i < samples ) { - } + sum += 2 * weight; - influences.sort( absNumericalSort ); + } - for ( let i = 0; i < 8; i ++ ) { + } - if ( i < length && influences[ i ][ 1 ] ) { + for ( let i = 0; i < weights.length; i ++ ) { - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; + weights[ i ] = weights[ i ] / sum; - } else { + } - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; + blurUniforms[ 'envMap' ].value = targetIn.texture; + blurUniforms[ 'samples' ].value = samples; + blurUniforms[ 'weights' ].value = weights; + blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - } + if ( poleAxis ) { + + blurUniforms[ 'poleAxis' ].value = poleAxis; } - workInfluences.sort( numericalSort ); + const { _lodMax } = this; + blurUniforms[ 'dTheta' ].value = radiansPerPixel; + blurUniforms[ 'mipInt' ].value = _lodMax - lodIn; - const morphTargets = material.morphTargets && geometry.morphAttributes.position; - const morphNormals = material.morphNormals && geometry.morphAttributes.normal; + const outputSize = this._sizeLods[ lodOut ]; + const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); + const y = 4 * ( this._cubeSize - outputSize ); - let morphInfluencesSum = 0; + _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); + renderer.setRenderTarget( targetOut ); + renderer.render( blurMesh, _flatCamera ); - for ( let i = 0; i < 8; i ++ ) { + } - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; + } - if ( index !== Number.MAX_SAFE_INTEGER && value ) { - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); + function _createPlanes( lodMax ) { - } + const lodPlanes = []; + const sizeLods = []; + const sigmas = []; - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { + let lod = lodMax; - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); + const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - } + for ( let i = 0; i < totalLods; i ++ ) { - morphInfluences[ i ] = value; - morphInfluencesSum += value; + const sizeLod = Math.pow( 2, lod ); + sizeLods.push( sizeLod ); + let sigma = 1.0 / sizeLod; - } else { + if ( i > lodMax - LOD_MIN ) { - if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { + sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; - geometry.deleteAttribute( 'morphTarget' + i ); + } else if ( i === 0 ) { - } + sigma = 0; - if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { + } - geometry.deleteAttribute( 'morphNormal' + i ); + sigmas.push( sigma ); - } + const texelSize = 1.0 / ( sizeLod - 1 ); + const min = - texelSize / 2; + const max = 1 + texelSize / 2; + const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - morphInfluences[ i ] = 0; + const cubeFaces = 6; + const vertices = 6; + const positionSize = 3; + const uvSize = 2; + const faceIndexSize = 1; - } + const position = new Float32Array( positionSize * vertices * cubeFaces ); + const uv = new Float32Array( uvSize * vertices * cubeFaces ); + const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); + + for ( let face = 0; face < cubeFaces; face ++ ) { + + const x = ( face % 3 ) * 2 / 3 - 1; + const y = face > 2 ? 0 : - 1; + const coordinates = [ + x, y, 0, + x + 2 / 3, y, 0, + x + 2 / 3, y + 1, 0, + x, y, 0, + x + 2 / 3, y + 1, 0, + x, y + 1, 0 + ]; + position.set( coordinates, positionSize * vertices * face ); + uv.set( uv1, uvSize * vertices * face ); + const fill = [ face, face, face, face, face, face ]; + faceIndex.set( fill, faceIndexSize * vertices * face ); } - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; + const planes = new BufferGeometry(); + planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); + planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); + planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); + lodPlanes.push( planes ); - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); + if ( lod > LOD_MIN ) { - } + lod --; - return { + } - update: update + } - }; + return { lodPlanes, sizeLods, sigmas }; } - function WebGLObjects( gl, geometries, attributes, info ) { + function _createRenderTarget( width, height, params ) { - let updateMap = new WeakMap(); + const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); + cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; + cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; + cubeUVRenderTarget.scissorTest = true; + return cubeUVRenderTarget; - function update( object ) { + } - const frame = info.render.frame; + function _setViewport( target, x, y, width, height ) { - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); + target.viewport.set( x, y, width, height ); + target.scissor.set( x, y, width, height ); - // Update once per frame + } - if ( updateMap.get( buffergeometry ) !== frame ) { + function _getBlurShader( lodMax, width, height ) { - geometries.update( buffergeometry ); + const weights = new Float32Array( MAX_SAMPLES ); + const poleAxis = new Vector3( 0, 1, 0 ); + const shaderMaterial = new ShaderMaterial( { - updateMap.set( buffergeometry, frame ); + name: 'SphericalGaussianBlur', - } + defines: { + 'n': MAX_SAMPLES, + 'CUBEUV_TEXEL_WIDTH': 1.0 / width, + 'CUBEUV_TEXEL_HEIGHT': 1.0 / height, + 'CUBEUV_MAX_MIP': `${lodMax}.0`, + }, - if ( object.isInstancedMesh ) { + uniforms: { + 'envMap': { value: null }, + 'samples': { value: 1 }, + 'weights': { value: weights }, + 'latitudinal': { value: false }, + 'dTheta': { value: 0 }, + 'mipInt': { value: 0 }, + 'poleAxis': { value: poleAxis } + }, - if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { + vertexShader: _getCommonVertexShader(), - object.addEventListener( 'dispose', onInstancedMeshDispose ); + fragmentShader: /* glsl */` - } + precision mediump float; + precision mediump int; - attributes.update( object.instanceMatrix, 34962 ); + varying vec3 vOutputDirection; - if ( object.instanceColor !== null ) { + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; - attributes.update( object.instanceColor, 34962 ); + #define ENVMAP_TYPE_CUBE_UV + #include - } + vec3 getSample( float theta, vec3 axis ) { - } + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - return buffergeometry; + return bilinearCubeUV( envMap, sampleDirection, mipInt ); - } + } - function dispose() { + void main() { - updateMap = new WeakMap(); + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - } + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - function onInstancedMeshDispose( event ) { + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - const instancedMesh = event.target; + } - instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); + axis = normalize( axis ); - attributes.remove( instancedMesh.instanceMatrix ); + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); + for ( int i = 1; i < n; i++ ) { - } + if ( i >= samples ) { - return { + break; - update: update, - dispose: dispose + } - }; + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - } + } - class DataTexture2DArray extends Texture { + } + `, - constructor( data = null, width = 1, height = 1, depth = 1 ) { + blending: NoBlending, + depthTest: false, + depthWrite: false - super( null ); + } ); - this.image = { data, width, height, depth }; + return shaderMaterial; - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + } - this.wrapR = ClampToEdgeWrapping; + function _getEquirectMaterial() { - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + return new ShaderMaterial( { - this.needsUpdate = true; + name: 'EquirectangularToCubeUV', - } + uniforms: { + 'envMap': { value: null } + }, - } + vertexShader: _getCommonVertexShader(), - DataTexture2DArray.prototype.isDataTexture2DArray = true; + fragmentShader: /* glsl */` - class DataTexture3D extends Texture { + precision mediump float; + precision mediump int; - constructor( data = null, width = 1, height = 1, depth = 1 ) { + varying vec3 vOutputDirection; - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 + uniform sampler2D envMap; - super( null ); + #include - this.image = { data, width, height, depth }; + void main() { - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); - this.wrapR = ClampToEdgeWrapping; + gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + } + `, - this.needsUpdate = true; + blending: NoBlending, + depthTest: false, + depthWrite: false - } + } ); } - DataTexture3D.prototype.isDataTexture3D = true; + function _getCubemapMaterial() { - /** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ + return new ShaderMaterial( { - const emptyTexture = new Texture(); - const emptyTexture2dArray = new DataTexture2DArray(); - const emptyTexture3d = new DataTexture3D(); - const emptyCubeTexture = new CubeTexture(); + name: 'CubemapToCubeUV', - // --- Utilities --- + uniforms: { + 'envMap': { value: null }, + 'flipEnvMap': { value: - 1 } + }, - // Array Caches (provide typed arrays for temporary by size) + vertexShader: _getCommonVertexShader(), - const arrayCacheF32 = []; - const arrayCacheI32 = []; + fragmentShader: /* glsl */` - // Float32Array caches used for uploading Matrix uniforms + precision mediump float; + precision mediump int; - const mat4array = new Float32Array( 16 ); - const mat3array = new Float32Array( 9 ); - const mat2array = new Float32Array( 4 ); + uniform float flipEnvMap; - // Flattening for arrays of vectors and matrices + varying vec3 vOutputDirection; - function flatten( array, nBlocks, blockSize ) { + uniform samplerCube envMap; - const firstElem = array[ 0 ]; + void main() { - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 + gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; + } + `, - if ( r === undefined ) { + blending: NoBlending, + depthTest: false, + depthWrite: false - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; + } ); - } + } - if ( nBlocks !== 0 ) { + function _getCommonVertexShader() { - firstElem.toArray( r, 0 ); + return /* glsl */` - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { + precision mediump float; + precision mediump int; - offset += blockSize; - array[ i ].toArray( r, offset ); + attribute float faceIndex; - } + varying vec3 vOutputDirection; - } + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { - return r; + uv = 2.0 * uv - 1.0; - } + vec3 direction = vec3( uv, 1.0 ); - function arraysEqual( a, b ) { + if ( face == 0.0 ) { - if ( a.length !== b.length ) return false; + direction = direction.zyx; // ( 1, v, u ) pos x - for ( let i = 0, l = a.length; i < l; i ++ ) { + } else if ( face == 1.0 ) { - if ( a[ i ] !== b[ i ] ) return false; + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y - } + } else if ( face == 2.0 ) { - return true; + direction.x *= -1.0; // ( -u, v, 1 ) pos z - } + } else if ( face == 3.0 ) { - function copyArray( a, b ) { + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x - for ( let i = 0, l = b.length; i < l; i ++ ) { + } else if ( face == 4.0 ) { - a[ i ] = b[ i ]; + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y - } + } else if ( face == 5.0 ) { - } + direction.z *= -1.0; // ( u, v, -1 ) neg z - // Texture unit allocation + } - function allocTexUnits( textures, n ) { + return direction; - let r = arrayCacheI32[ n ]; + } - if ( r === undefined ) { + void main() { - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); } + `; - for ( let i = 0; i !== n; ++ i ) { - - r[ i ] = textures.allocateTextureUnit(); + } - } + function WebGLCubeUVMaps( renderer ) { - return r; + let cubeUVmaps = new WeakMap(); - } + let pmremGenerator = null; - // --- Setters --- + function get( texture ) { - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. + if ( texture && texture.isTexture ) { - // Single scalar + const mapping = texture.mapping; - function setValueV1f( gl, v ) { + const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); + const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); - const cache = this.cache; + // equirect/cube map to cubeUV conversion - if ( cache[ 0 ] === v ) return; + if ( isEquirectMap || isCubeMap ) { - gl.uniform1f( this.addr, v ); + if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { - cache[ 0 ] = v; + texture.needsPMREMUpdate = false; - } + let renderTarget = cubeUVmaps.get( texture ); - // Single float vector (from flat array or THREE.VectorN) + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - function setValueV2f( gl, v ) { + renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); + cubeUVmaps.set( texture, renderTarget ); - const cache = this.cache; + return renderTarget.texture; - if ( v.x !== undefined ) { + } else { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { + if ( cubeUVmaps.has( texture ) ) { - gl.uniform2f( this.addr, v.x, v.y ); + return cubeUVmaps.get( texture ).texture; - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; + } else { - } + const image = texture.image; - } else { + if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { - if ( arraysEqual( cache, v ) ) return; + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - gl.uniform2fv( this.addr, v ); + const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); + cubeUVmaps.set( texture, renderTarget ); - copyArray( cache, v ); + texture.addEventListener( 'dispose', onTextureDispose ); - } + return renderTarget.texture; - } + } else { - function setValueV3f( gl, v ) { + // image not yet ready. try the conversion next frame - const cache = this.cache; + return null; - if ( v.x !== undefined ) { + } - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { + } - gl.uniform3f( this.addr, v.x, v.y, v.z ); + } - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; + } } - } else if ( v.r !== undefined ) { + return texture; - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { + } - gl.uniform3f( this.addr, v.r, v.g, v.b ); + function isCubeTextureComplete( image ) { - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; + let count = 0; + const length = 6; - } + for ( let i = 0; i < length; i ++ ) { - } else { + if ( image[ i ] !== undefined ) count ++; - if ( arraysEqual( cache, v ) ) return; + } - gl.uniform3fv( this.addr, v ); + return count === length; - copyArray( cache, v ); } - } - - function setValueV4f( gl, v ) { + function onTextureDispose( event ) { - const cache = this.cache; + const texture = event.target; - if ( v.x !== undefined ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { + const cubemapUV = cubeUVmaps.get( texture ); - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + if ( cubemapUV !== undefined ) { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; + cubeUVmaps.delete( texture ); + cubemapUV.dispose(); } - } else { + } - if ( arraysEqual( cache, v ) ) return; + function dispose() { - gl.uniform4fv( this.addr, v ); + cubeUVmaps = new WeakMap(); - copyArray( cache, v ); + if ( pmremGenerator !== null ) { + + pmremGenerator.dispose(); + pmremGenerator = null; + + } } + return { + get: get, + dispose: dispose + }; + } - // Single matrix (from flat array or THREE.MatrixN) + function WebGLExtensions( gl ) { - function setValueM2( gl, v ) { + const extensions = {}; - const cache = this.cache; - const elements = v.elements; + function getExtension( name ) { - if ( elements === undefined ) { + if ( extensions[ name ] !== undefined ) { - if ( arraysEqual( cache, v ) ) return; + return extensions[ name ]; - gl.uniformMatrix2fv( this.addr, false, v ); + } - copyArray( cache, v ); + let extension; - } else { + switch ( name ) { - if ( arraysEqual( cache, elements ) ) return; + case 'WEBGL_depth_texture': + extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); + break; - mat2array.set( elements ); + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; - gl.uniformMatrix2fv( this.addr, false, mat2array ); + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; - copyArray( cache, elements ); + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - } + default: + extension = gl.getExtension( name ); - } + } - function setValueM3( gl, v ) { + extensions[ name ] = extension; - const cache = this.cache; - const elements = v.elements; + return extension; - if ( elements === undefined ) { + } - if ( arraysEqual( cache, v ) ) return; + return { - gl.uniformMatrix3fv( this.addr, false, v ); + has: function ( name ) { - copyArray( cache, v ); + return getExtension( name ) !== null; - } else { + }, - if ( arraysEqual( cache, elements ) ) return; + init: function ( capabilities ) { - mat3array.set( elements ); + if ( capabilities.isWebGL2 ) { - gl.uniformMatrix3fv( this.addr, false, mat3array ); + getExtension( 'EXT_color_buffer_float' ); - copyArray( cache, elements ); + } else { - } + getExtension( 'WEBGL_depth_texture' ); + getExtension( 'OES_texture_float' ); + getExtension( 'OES_texture_half_float' ); + getExtension( 'OES_texture_half_float_linear' ); + getExtension( 'OES_standard_derivatives' ); + getExtension( 'OES_element_index_uint' ); + getExtension( 'OES_vertex_array_object' ); + getExtension( 'ANGLE_instanced_arrays' ); - } + } - function setValueM4( gl, v ) { + getExtension( 'OES_texture_float_linear' ); + getExtension( 'EXT_color_buffer_half_float' ); + getExtension( 'WEBGL_multisampled_render_to_texture' ); - const cache = this.cache; - const elements = v.elements; + }, - if ( elements === undefined ) { + get: function ( name ) { - if ( arraysEqual( cache, v ) ) return; + const extension = getExtension( name ); - gl.uniformMatrix4fv( this.addr, false, v ); + if ( extension === null ) { - copyArray( cache, v ); + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - } else { + } - if ( arraysEqual( cache, elements ) ) return; + return extension; - mat4array.set( elements ); + } - gl.uniformMatrix4fv( this.addr, false, mat4array ); + }; - copyArray( cache, elements ); + } - } + function WebGLGeometries( gl, attributes, info, bindingStates ) { - } + const geometries = {}; + const wireframeAttributes = new WeakMap(); - // Single integer / boolean + function onGeometryDispose( event ) { - function setValueV1i( gl, v ) { + const geometry = event.target; - const cache = this.cache; + if ( geometry.index !== null ) { - if ( cache[ 0 ] === v ) return; + attributes.remove( geometry.index ); - gl.uniform1i( this.addr, v ); + } - cache[ 0 ] = v; + for ( const name in geometry.attributes ) { - } + attributes.remove( geometry.attributes[ name ] ); - // Single integer / boolean vector (from flat array) + } - function setValueV2i( gl, v ) { + geometry.removeEventListener( 'dispose', onGeometryDispose ); - const cache = this.cache; + delete geometries[ geometry.id ]; - if ( arraysEqual( cache, v ) ) return; + const attribute = wireframeAttributes.get( geometry ); - gl.uniform2iv( this.addr, v ); + if ( attribute ) { - copyArray( cache, v ); + attributes.remove( attribute ); + wireframeAttributes.delete( geometry ); - } + } - function setValueV3i( gl, v ) { + bindingStates.releaseStatesOfGeometry( geometry ); - const cache = this.cache; + if ( geometry.isInstancedBufferGeometry === true ) { - if ( arraysEqual( cache, v ) ) return; + delete geometry._maxInstanceCount; - gl.uniform3iv( this.addr, v ); + } - copyArray( cache, v ); + // - } + info.memory.geometries --; - function setValueV4i( gl, v ) { + } - const cache = this.cache; + function get( object, geometry ) { - if ( arraysEqual( cache, v ) ) return; + if ( geometries[ geometry.id ] === true ) return geometry; - gl.uniform4iv( this.addr, v ); + geometry.addEventListener( 'dispose', onGeometryDispose ); - copyArray( cache, v ); + geometries[ geometry.id ] = true; - } + info.memory.geometries ++; - // Single unsigned integer + return geometry; - function setValueV1ui( gl, v ) { + } - const cache = this.cache; + function update( geometry ) { - if ( cache[ 0 ] === v ) return; + const geometryAttributes = geometry.attributes; - gl.uniform1ui( this.addr, v ); + // Updating index buffer in VAO now. See WebGLBindingStates. - cache[ 0 ] = v; + for ( const name in geometryAttributes ) { - } + attributes.update( geometryAttributes[ name ], 34962 ); - // Single unsigned integer vector (from flat array) + } - function setValueV2ui( gl, v ) { + // morph targets - const cache = this.cache; + const morphAttributes = geometry.morphAttributes; - if ( arraysEqual( cache, v ) ) return; + for ( const name in morphAttributes ) { - gl.uniform2uiv( this.addr, v ); + const array = morphAttributes[ name ]; - copyArray( cache, v ); + for ( let i = 0, l = array.length; i < l; i ++ ) { - } + attributes.update( array[ i ], 34962 ); - function setValueV3ui( gl, v ) { + } - const cache = this.cache; + } - if ( arraysEqual( cache, v ) ) return; + } - gl.uniform3uiv( this.addr, v ); + function updateWireframeAttribute( geometry ) { - copyArray( cache, v ); + const indices = []; - } + const geometryIndex = geometry.index; + const geometryPosition = geometry.attributes.position; + let version = 0; - function setValueV4ui( gl, v ) { + if ( geometryIndex !== null ) { - const cache = this.cache; + const array = geometryIndex.array; + version = geometryIndex.version; - if ( arraysEqual( cache, v ) ) return; + for ( let i = 0, l = array.length; i < l; i += 3 ) { - gl.uniform4uiv( this.addr, v ); + const a = array[ i + 0 ]; + const b = array[ i + 1 ]; + const c = array[ i + 2 ]; - copyArray( cache, v ); + indices.push( a, b, b, c, c, a ); - } + } + } else { - // Single texture (2D / Cube) + const array = geometryPosition.array; + version = geometryPosition.version; - function setValueT1( gl, v, textures ) { + for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const a = i + 0; + const b = i + 1; + const c = i + 2; - if ( cache[ 0 ] !== unit ) { + indices.push( a, b, b, c, c, a ); - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + } - } + } - textures.safeSetTexture2D( v || emptyTexture, unit ); + const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); + attribute.version = version; - } + // Updating index buffer in VAO now. See WebGLBindingStates - function setValueT3D1( gl, v, textures ) { + // - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const previousAttribute = wireframeAttributes.get( geometry ); - if ( cache[ 0 ] !== unit ) { + if ( previousAttribute ) attributes.remove( previousAttribute ); - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + // + + wireframeAttributes.set( geometry, attribute ); } - textures.setTexture3D( v || emptyTexture3d, unit ); + function getWireframeAttribute( geometry ) { - } + const currentAttribute = wireframeAttributes.get( geometry ); - function setValueT6( gl, v, textures ) { + if ( currentAttribute ) { - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const geometryIndex = geometry.index; - if ( cache[ 0 ] !== unit ) { + if ( geometryIndex !== null ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + // if the attribute is obsolete, create a new one - } + if ( currentAttribute.version < geometryIndex.version ) { - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); + updateWireframeAttribute( geometry ); - } + } - function setValueT2DArray1( gl, v, textures ) { + } - const cache = this.cache; - const unit = textures.allocateTextureUnit(); - - if ( cache[ 0 ] !== unit ) { - - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + } else { - } + updateWireframeAttribute( geometry ); - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); + } - } + return wireframeAttributes.get( geometry ); - // Helper to pick the right setter for the singular case + } - function getSingularSetter( type ) { + return { - switch ( type ) { + get: get, + update: update, - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 + getWireframeAttribute: getWireframeAttribute - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 + }; - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 + } - case 0x1405: return setValueV1ui; // UINT - case 0x8dc6: return setValueV2ui; // _VEC2 - case 0x8dc7: return setValueV3ui; // _VEC3 - case 0x8dc8: return setValueV4ui; // _VEC4 + function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; + const isWebGL2 = capabilities.isWebGL2; - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; + let mode; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; + function setMode( value ) { - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; + mode = value; } - } - - - // Array of scalars - - function setValueV1fArray( gl, v ) { + let type, bytesPerElement; - gl.uniform1fv( this.addr, v ); + function setIndex( value ) { - } + type = value.type; + bytesPerElement = value.bytesPerElement; - // Array of vectors (from flat array or array of THREE.VectorN) + } - function setValueV2fArray( gl, v ) { + function render( start, count ) { - const data = flatten( v, this.size, 2 ); + gl.drawElements( mode, count, type, start * bytesPerElement ); - gl.uniform2fv( this.addr, data ); + info.update( count, mode, 1 ); - } + } - function setValueV3fArray( gl, v ) { + function renderInstances( start, count, primcount ) { - const data = flatten( v, this.size, 3 ); + if ( primcount === 0 ) return; - gl.uniform3fv( this.addr, data ); + let extension, methodName; - } + if ( isWebGL2 ) { - function setValueV4fArray( gl, v ) { + extension = gl; + methodName = 'drawElementsInstanced'; - const data = flatten( v, this.size, 4 ); + } else { - gl.uniform4fv( this.addr, data ); + extension = extensions.get( 'ANGLE_instanced_arrays' ); + methodName = 'drawElementsInstancedANGLE'; - } + if ( extension === null ) { - // Array of matrices (from flat array or array of THREE.MatrixN) + console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - function setValueM2Array( gl, v ) { + } - const data = flatten( v, this.size, 4 ); + } - gl.uniformMatrix2fv( this.addr, false, data ); + extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - } + info.update( count, mode, primcount ); - function setValueM3Array( gl, v ) { + } - const data = flatten( v, this.size, 9 ); + // - gl.uniformMatrix3fv( this.addr, false, data ); + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; } - function setValueM4Array( gl, v ) { - - const data = flatten( v, this.size, 16 ); - - gl.uniformMatrix4fv( this.addr, false, data ); - - } + function WebGLInfo( gl ) { - // Array of integer / boolean + const memory = { + geometries: 0, + textures: 0 + }; - function setValueV1iArray( gl, v ) { + const render = { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0 + }; - gl.uniform1iv( this.addr, v ); + function update( count, mode, instanceCount ) { - } + render.calls ++; - // Array of integer / boolean vectors (from flat array) + switch ( mode ) { - function setValueV2iArray( gl, v ) { + case 4: + render.triangles += instanceCount * ( count / 3 ); + break; - gl.uniform2iv( this.addr, v ); + case 1: + render.lines += instanceCount * ( count / 2 ); + break; - } + case 3: + render.lines += instanceCount * ( count - 1 ); + break; - function setValueV3iArray( gl, v ) { + case 2: + render.lines += instanceCount * count; + break; - gl.uniform3iv( this.addr, v ); + case 0: + render.points += instanceCount * count; + break; - } + default: + console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); + break; - function setValueV4iArray( gl, v ) { + } - gl.uniform4iv( this.addr, v ); + } - } + function reset() { - // Array of unsigned integer + render.frame ++; + render.calls = 0; + render.triangles = 0; + render.points = 0; + render.lines = 0; - function setValueV1uiArray( gl, v ) { + } - gl.uniform1uiv( this.addr, v ); + return { + memory: memory, + render: render, + programs: null, + autoReset: true, + reset: reset, + update: update + }; } - // Array of unsigned integer vectors (from flat array) - - function setValueV2uiArray( gl, v ) { + function numericalSort( a, b ) { - gl.uniform2uiv( this.addr, v ); + return a[ 0 ] - b[ 0 ]; } - function setValueV3uiArray( gl, v ) { + function absNumericalSort( a, b ) { - gl.uniform3uiv( this.addr, v ); + return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } - function setValueV4uiArray( gl, v ) { - - gl.uniform4uiv( this.addr, v ); + function denormalize( morph, attribute ) { - } + let denominator = 1; + const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; + if ( array instanceof Int8Array ) denominator = 127; + else if ( array instanceof Int16Array ) denominator = 32767; + else if ( array instanceof Int32Array ) denominator = 2147483647; + else console.error( 'THREE.WebGLMorphtargets: Unsupported morph attribute data type: ', array ); - // Array of textures (2D / Cube) + morph.divideScalar( denominator ); - function setValueT1Array( gl, v, textures ) { + } - const n = v.length; + function WebGLMorphtargets( gl, capabilities, textures ) { - const units = allocTexUnits( textures, n ); + const influencesList = {}; + const morphInfluences = new Float32Array( 8 ); + const morphTextures = new WeakMap(); + const morph = new Vector4(); - gl.uniform1iv( this.addr, units ); + const workInfluences = []; - for ( let i = 0; i !== n; ++ i ) { + for ( let i = 0; i < 8; i ++ ) { - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); + workInfluences[ i ] = [ i, 0 ]; } - } - - function setValueT6Array( gl, v, textures ) { - - const n = v.length; - - const units = allocTexUnits( textures, n ); - - gl.uniform1iv( this.addr, units ); + function update( object, geometry, material, program ) { - for ( let i = 0; i !== n; ++ i ) { + const objectInfluences = object.morphTargetInfluences; - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); + if ( capabilities.isWebGL2 === true ) { - } + // instead of using attributes, the WebGL 2 code path encodes morph targets + // into an array of data textures. Each layer represents a single morph target. - } + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - // Helper to pick the right setter for a pure (bottom-level) array + let entry = morphTextures.get( geometry ); - function getPureArraySetter( type ) { + if ( entry === undefined || entry.count !== morphTargetsCount ) { - switch ( type ) { + if ( entry !== undefined ) entry.texture.dispose(); - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 + const hasMorphPosition = geometry.morphAttributes.position !== undefined; + const hasMorphNormals = geometry.morphAttributes.normal !== undefined; + const hasMorphColors = geometry.morphAttributes.color !== undefined; - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 + const morphTargets = geometry.morphAttributes.position || []; + const morphNormals = geometry.morphAttributes.normal || []; + const morphColors = geometry.morphAttributes.color || []; - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 + let vertexDataCount = 0; - case 0x1405: return setValueV1uiArray; // UINT - case 0x8dc6: return setValueV2uiArray; // _VEC2 - case 0x8dc7: return setValueV3uiArray; // _VEC3 - case 0x8dc8: return setValueV4uiArray; // _VEC4 + if ( hasMorphPosition === true ) vertexDataCount = 1; + if ( hasMorphNormals === true ) vertexDataCount = 2; + if ( hasMorphColors === true ) vertexDataCount = 3; - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; + let width = geometry.attributes.position.count * vertexDataCount; + let height = 1; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; + if ( width > capabilities.maxTextureSize ) { - } + height = Math.ceil( width / capabilities.maxTextureSize ); + width = capabilities.maxTextureSize; - } + } - // --- Uniform Classes --- + const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); - function SingleUniform( id, activeInfo, addr ) { + const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); + texture.type = FloatType; + texture.needsUpdate = true; - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); + // fill buffer - // this.path = activeInfo.name; // DEBUG + const vertexDataStride = vertexDataCount * 4; - } + for ( let i = 0; i < morphTargetsCount; i ++ ) { - function PureArrayUniform( id, activeInfo, addr ) { + const morphTarget = morphTargets[ i ]; + const morphNormal = morphNormals[ i ]; + const morphColor = morphColors[ i ]; - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); + const offset = width * height * 4 * i; - // this.path = activeInfo.name; // DEBUG + for ( let j = 0; j < morphTarget.count; j ++ ) { - } + const stride = j * vertexDataStride; - PureArrayUniform.prototype.updateCache = function ( data ) { + if ( hasMorphPosition === true ) { - const cache = this.cache; + morph.fromBufferAttribute( morphTarget, j ); - if ( data instanceof Float32Array && cache.length !== data.length ) { + if ( morphTarget.normalized === true ) denormalize( morph, morphTarget ); - this.cache = new Float32Array( data.length ); + buffer[ offset + stride + 0 ] = morph.x; + buffer[ offset + stride + 1 ] = morph.y; + buffer[ offset + stride + 2 ] = morph.z; + buffer[ offset + stride + 3 ] = 0; - } + } - copyArray( cache, data ); + if ( hasMorphNormals === true ) { - }; + morph.fromBufferAttribute( morphNormal, j ); - function StructuredUniform( id ) { + if ( morphNormal.normalized === true ) denormalize( morph, morphNormal ); - this.id = id; + buffer[ offset + stride + 4 ] = morph.x; + buffer[ offset + stride + 5 ] = morph.y; + buffer[ offset + stride + 6 ] = morph.z; + buffer[ offset + stride + 7 ] = 0; - this.seq = []; - this.map = {}; + } - } + if ( hasMorphColors === true ) { - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { + morph.fromBufferAttribute( morphColor, j ); - const seq = this.seq; + if ( morphColor.normalized === true ) denormalize( morph, morphColor ); - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + buffer[ offset + stride + 8 ] = morph.x; + buffer[ offset + stride + 9 ] = morph.y; + buffer[ offset + stride + 10 ] = morph.z; + buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); + } - } + } - }; + } - // --- Top-level --- + entry = { + count: morphTargetsCount, + texture: texture, + size: new Vector2( width, height ) + }; - // Parser - builds up the property tree from the path strings + morphTextures.set( geometry, entry ); - const RePathPart = /(\w+)(\])?(\[|\.)?/g; + function disposeTexture() { - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. + texture.dispose(); - function addUniform( container, uniformObject ) { + morphTextures.delete( geometry ); - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; + geometry.removeEventListener( 'dispose', disposeTexture ); - } + } - function parseUniform( activeInfo, addr, container ) { + geometry.addEventListener( 'dispose', disposeTexture ); - const path = activeInfo.name, - pathLength = path.length; + } - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; + // - while ( true ) { + let morphInfluencesSum = 0; - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; + for ( let i = 0; i < objectInfluences.length; i ++ ) { - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; + morphInfluencesSum += objectInfluences[ i ]; - if ( idIsIndex ) id = id | 0; // convert to integer + } - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - // bare name or "pure" bottom-level array "[0]" suffix + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences ); - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); + program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures ); + program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size ); - break; } else { - // step into inner node / create it in case it doesn't exist - - const map = container.map; - let next = map[ id ]; + // When object doesn't have morph target influences defined, we treat it as a 0-length array + // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - if ( next === undefined ) { + const length = objectInfluences === undefined ? 0 : objectInfluences.length; - next = new StructuredUniform( id ); - addUniform( container, next ); + let influences = influencesList[ geometry.id ]; - } + if ( influences === undefined || influences.length !== length ) { - container = next; + // initialise list - } + influences = []; - } + for ( let i = 0; i < length; i ++ ) { - } + influences[ i ] = [ i, 0 ]; - // Root Container + } - function WebGLUniforms( gl, program ) { + influencesList[ geometry.id ] = influences; - this.seq = []; - this.map = {}; + } - const n = gl.getProgramParameter( program, 35718 ); + // Collect influences - for ( let i = 0; i < n; ++ i ) { + for ( let i = 0; i < length; i ++ ) { - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); + const influence = influences[ i ]; - parseUniform( info, addr, this ); + influence[ 0 ] = i; + influence[ 1 ] = objectInfluences[ i ]; - } + } - } + influences.sort( absNumericalSort ); - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { + for ( let i = 0; i < 8; i ++ ) { - const u = this.map[ name ]; + if ( i < length && influences[ i ][ 1 ] ) { - if ( u !== undefined ) u.setValue( gl, value, textures ); + workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; + workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - }; + } else { - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { + workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; + workInfluences[ i ][ 1 ] = 0; - const v = object[ name ]; + } - if ( v !== undefined ) this.setValue( gl, name, v ); + } - }; + workInfluences.sort( numericalSort ); + const morphTargets = geometry.morphAttributes.position; + const morphNormals = geometry.morphAttributes.normal; - // Static interface + let morphInfluencesSum = 0; - WebGLUniforms.upload = function ( gl, seq, values, textures ) { + for ( let i = 0; i < 8; i ++ ) { - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + const influence = workInfluences[ i ]; + const index = influence[ 0 ]; + const value = influence[ 1 ]; - const u = seq[ i ], - v = values[ u.id ]; + if ( index !== Number.MAX_SAFE_INTEGER && value ) { - if ( v.needsUpdate !== false ) { + if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); + geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - } + } - } + if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - }; + geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - WebGLUniforms.seqWithValue = function ( seq, values ) { + } - const r = []; + morphInfluences[ i ] = value; + morphInfluencesSum += value; - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + } else { - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); + if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { - } + geometry.deleteAttribute( 'morphTarget' + i ); - return r; + } - }; + if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { - function WebGLShader( gl, type, string ) { + geometry.deleteAttribute( 'morphNormal' + i ); - const shader = gl.createShader( type ); + } - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + morphInfluences[ i ] = 0; - return shader; + } - } + } - let programIdCount = 0; + // GLSL shader uses formula baseinfluence * base + sum(target * influence) + // This allows us to switch between absolute morphs and relative morphs without changing shader code + // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - function addLineNumbers( string ) { + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - const lines = string.split( '\n' ); + } - for ( let i = 0; i < lines.length; i ++ ) { + } - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + return { - } + update: update - return lines.join( '\n' ); + }; } - function getEncodingComponents( encoding ) { + function WebGLObjects( gl, geometries, attributes, info ) { - switch ( encoding ) { + let updateMap = new WeakMap(); - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; + function update( object ) { - } + const frame = info.render.frame; - } + const geometry = object.geometry; + const buffergeometry = geometries.get( object, geometry ); - function getShaderErrors( gl, shader, type ) { + // Update once per frame - const status = gl.getShaderParameter( shader, 35713 ); - const log = gl.getShaderInfoLog( shader ).trim(); + if ( updateMap.get( buffergeometry ) !== frame ) { - if ( status && log === '' ) return ''; + geometries.update( buffergeometry ); - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + updateMap.set( buffergeometry, frame ); - const source = gl.getShaderSource( shader ); + } - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); + if ( object.isInstancedMesh ) { - } + if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { - function getTexelDecodingFunction( functionName, encoding ) { + object.addEventListener( 'dispose', onInstancedMeshDispose ); - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; + } - } + attributes.update( object.instanceMatrix, 34962 ); - function getTexelEncodingFunction( functionName, encoding ) { + if ( object.instanceColor !== null ) { - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; + attributes.update( object.instanceColor, 34962 ); - } + } - function getToneMappingFunction( functionName, toneMapping ) { + } - let toneMappingName; + return buffergeometry; - switch ( toneMapping ) { + } - case LinearToneMapping: - toneMappingName = 'Linear'; - break; + function dispose() { - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; + updateMap = new WeakMap(); - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; + } - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; + function onInstancedMeshDispose( event ) { - case CustomToneMapping: - toneMappingName = 'Custom'; - break; + const instancedMesh = event.target; - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; + instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); - } + attributes.remove( instancedMesh.instanceMatrix ); - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); - } + } - function generateExtensions( parameters ) { + return { - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; + update: update, + dispose: dispose - return chunks.filter( filterEmptyLine ).join( '\n' ); + }; } - function generateDefines( defines ) { + /** + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [textures] ) + * + * uploads a uniform value(s) + * the 'textures' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (textures factorizations): + * + * .upload( gl, seq, values, textures ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * + * Methods of the top-level container (textures factorizations): + * + * .setValue( gl, name, value, textures ) + * + * sets uniform with name 'name' to 'value' + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + */ - const chunks = []; + const emptyTexture = new Texture(); + const emptyArrayTexture = new DataArrayTexture(); + const empty3dTexture = new Data3DTexture(); + const emptyCubeTexture = new CubeTexture(); - for ( const name in defines ) { + // --- Utilities --- - const value = defines[ name ]; + // Array Caches (provide typed arrays for temporary by size) - if ( value === false ) continue; + const arrayCacheF32 = []; + const arrayCacheI32 = []; - chunks.push( '#define ' + name + ' ' + value ); + // Float32Array caches used for uploading Matrix uniforms - } + const mat4array = new Float32Array( 16 ); + const mat3array = new Float32Array( 9 ); + const mat2array = new Float32Array( 4 ); - return chunks.join( '\n' ); + // Flattening for arrays of vectors and matrices - } + function flatten( array, nBlocks, blockSize ) { - function fetchAttributeLocations( gl, program ) { + const firstElem = array[ 0 ]; - const attributes = {}; + if ( firstElem <= 0 || firstElem > 0 ) return array; + // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 - const n = gl.getProgramParameter( program, 35721 ); + const n = nBlocks * blockSize; + let r = arrayCacheF32[ n ]; - for ( let i = 0; i < n; i ++ ) { + if ( r === undefined ) { - const info = gl.getActiveAttrib( program, i ); - const name = info.name; + r = new Float32Array( n ); + arrayCacheF32[ n ] = r; - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + } + + if ( nBlocks !== 0 ) { - attributes[ name ] = gl.getAttribLocation( program, name ); + firstElem.toArray( r, 0 ); - } + for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - return attributes; + offset += blockSize; + array[ i ].toArray( r, offset ); - } + } - function filterEmptyLine( string ) { + } - return string !== ''; + return r; } - function replaceLightNums( string, parameters ) { + function arraysEqual( a, b ) { - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); + if ( a.length !== b.length ) return false; - } + for ( let i = 0, l = a.length; i < l; i ++ ) { - function replaceClippingPlaneNums( string, parameters ) { + if ( a[ i ] !== b[ i ] ) return false; - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); + } + + return true; } - // Resolve Includes + function copyArray( a, b ) { - const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; + for ( let i = 0, l = b.length; i < l; i ++ ) { - function resolveIncludes( string ) { + a[ i ] = b[ i ]; - return string.replace( includePattern, includeReplacer ); + } } - function includeReplacer( match, include ) { - - const string = ShaderChunk[ include ]; + // Texture unit allocation - if ( string === undefined ) { + function allocTexUnits( textures, n ) { - throw new Error( 'Can not resolve #include <' + include + '>' ); + let r = arrayCacheI32[ n ]; - } + if ( r === undefined ) { - return resolveIncludes( string ); + r = new Int32Array( n ); + arrayCacheI32[ n ] = r; - } + } - // Unroll Loops + for ( let i = 0; i !== n; ++ i ) { - const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; + r[ i ] = textures.allocateTextureUnit(); - function unrollLoops( string ) { + } - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); + return r; } - function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); + // --- Setters --- - } + // Note: Defining these methods externally, because they come in a bunch + // and this way their names minify. - function loopReplacer( match, start, end, snippet ) { + // Single scalar - let string = ''; + function setValueV1f( gl, v ) { - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { + const cache = this.cache; - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); + if ( cache[ 0 ] === v ) return; - } + gl.uniform1f( this.addr, v ); - return string; + cache[ 0 ] = v; } - // + // Single float vector (from flat array or THREE.VectorN) - function generatePrecision( parameters ) { + function setValueV2f( gl, v ) { - let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; + const cache = this.cache; - if ( parameters.precision === 'highp' ) { + if ( v.x !== undefined ) { - precisionstring += '\n#define HIGH_PRECISION'; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - } else if ( parameters.precision === 'mediump' ) { + gl.uniform2f( this.addr, v.x, v.y ); - precisionstring += '\n#define MEDIUM_PRECISION'; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - } else if ( parameters.precision === 'lowp' ) { + } - precisionstring += '\n#define LOW_PRECISION'; + } else { - } + if ( arraysEqual( cache, v ) ) return; - return precisionstring; + gl.uniform2fv( this.addr, v ); - } + copyArray( cache, v ); - function generateShadowMapTypeDefine( parameters ) { + } - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + } - if ( parameters.shadowMapType === PCFShadowMap ) { + function setValueV3f( gl, v ) { - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + const cache = this.cache; - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { + if ( v.x !== undefined ) { - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - } else if ( parameters.shadowMapType === VSMShadowMap ) { + gl.uniform3f( this.addr, v.x, v.y, v.z ); - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - } + } - return shadowMapTypeDefine; + } else if ( v.r !== undefined ) { - } + if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - function generateEnvMapTypeDefine( parameters ) { + gl.uniform3f( this.addr, v.r, v.g, v.b ); - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + cache[ 0 ] = v.r; + cache[ 1 ] = v.g; + cache[ 2 ] = v.b; - if ( parameters.envMap ) { + } - switch ( parameters.envMapMode ) { + } else { - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; + if ( arraysEqual( cache, v ) ) return; - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; + gl.uniform3fv( this.addr, v ); - } + copyArray( cache, v ); } - return envMapTypeDefine; - } - function generateEnvMapModeDefine( parameters ) { + function setValueV4f( gl, v ) { - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + const cache = this.cache; - if ( parameters.envMap ) { + if ( v.x !== undefined ) { - switch ( parameters.envMapMode ) { + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - case CubeRefractionMapping: - case CubeUVRefractionMapping: + gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; } - } + } else { - return envMapModeDefine; + if ( arraysEqual( cache, v ) ) return; + + gl.uniform4fv( this.addr, v ); + + copyArray( cache, v ); + + } } - function generateEnvMapBlendingDefine( parameters ) { + // Single matrix (from flat array or THREE.MatrixN) - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; + function setValueM2( gl, v ) { - if ( parameters.envMap ) { + const cache = this.cache; + const elements = v.elements; - switch ( parameters.combine ) { + if ( elements === undefined ) { - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; + if ( arraysEqual( cache, v ) ) return; - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; + gl.uniformMatrix2fv( this.addr, false, v ); - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; + copyArray( cache, v ); - } + } else { - } + if ( arraysEqual( cache, elements ) ) return; - return envMapBlendingDefine; + mat2array.set( elements ); + + gl.uniformMatrix2fv( this.addr, false, mat2array ); + + copyArray( cache, elements ); + + } } - function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { + function setValueM3( gl, v ) { - const gl = renderer.getContext(); + const cache = this.cache; + const elements = v.elements; - const defines = parameters.defines; + if ( elements === undefined ) { - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; + if ( arraysEqual( cache, v ) ) return; - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + gl.uniformMatrix3fv( this.addr, false, v ); + copyArray( cache, v ); - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + } else { - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); + if ( arraysEqual( cache, elements ) ) return; - const customDefines = generateDefines( defines ); + mat3array.set( elements ); - const program = gl.createProgram(); + gl.uniformMatrix3fv( this.addr, false, mat3array ); - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; + copyArray( cache, elements ); - if ( parameters.isRawShaderMaterial ) { + } - prefixVertex = [ + } - customDefines + function setValueM4( gl, v ) { - ].filter( filterEmptyLine ).join( '\n' ); + const cache = this.cache; + const elements = v.elements; - if ( prefixVertex.length > 0 ) { + if ( elements === undefined ) { - prefixVertex += '\n'; + if ( arraysEqual( cache, v ) ) return; - } + gl.uniformMatrix4fv( this.addr, false, v ); - prefixFragment = [ + copyArray( cache, v ); - customExtensions, - customDefines + } else { - ].filter( filterEmptyLine ).join( '\n' ); + if ( arraysEqual( cache, elements ) ) return; - if ( prefixFragment.length > 0 ) { + mat4array.set( elements ); - prefixFragment += '\n'; + gl.uniformMatrix4fv( this.addr, false, mat4array ); - } + copyArray( cache, elements ); - } else { + } - prefixVertex = [ + } - generatePrecision( parameters ), + // Single integer / boolean - '#define SHADER_NAME ' + parameters.shaderName, + function setValueV1i( gl, v ) { - customDefines, + const cache = this.cache; - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', + if ( cache[ 0 ] === v ) return; - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + gl.uniform1i( this.addr, v ); - '#define GAMMA_FACTOR ' + gammaFactorDefine, + cache[ 0 ] = v; - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', + } - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', + // Single integer / boolean vector (from flat array) - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + function setValueV2i( gl, v ) { - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + const cache = this.cache; - parameters.flatShading ? '#define FLAT_SHADED' : '', + if ( arraysEqual( cache, v ) ) return; - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + gl.uniform2iv( this.addr, v ); - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + copyArray( cache, v ); - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + } - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + function setValueV3i( gl, v ) { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + const cache = this.cache; - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + if ( arraysEqual( cache, v ) ) return; - '#ifdef USE_INSTANCING', + gl.uniform3iv( this.addr, v ); - ' attribute mat4 instanceMatrix;', + copyArray( cache, v ); - '#endif', + } - '#ifdef USE_INSTANCING_COLOR', + function setValueV4i( gl, v ) { - ' attribute vec3 instanceColor;', + const cache = this.cache; - '#endif', + if ( arraysEqual( cache, v ) ) return; - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', + gl.uniform4iv( this.addr, v ); - '#ifdef USE_TANGENT', + copyArray( cache, v ); - ' attribute vec4 tangent;', + } - '#endif', + // Single unsigned integer - '#if defined( USE_COLOR_ALPHA )', + function setValueV1ui( gl, v ) { - ' attribute vec4 color;', + const cache = this.cache; - '#elif defined( USE_COLOR )', + if ( cache[ 0 ] === v ) return; - ' attribute vec3 color;', + gl.uniform1ui( this.addr, v ); - '#endif', + cache[ 0 ] = v; - '#ifdef USE_MORPHTARGETS', + } - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', + // Single unsigned integer vector (from flat array) - ' #ifdef USE_MORPHNORMALS', + function setValueV2ui( gl, v ) { - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', + const cache = this.cache; - ' #else', + if ( arraysEqual( cache, v ) ) return; - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', + gl.uniform2uiv( this.addr, v ); - ' #endif', + copyArray( cache, v ); - '#endif', + } - '#ifdef USE_SKINNING', + function setValueV3ui( gl, v ) { - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', + const cache = this.cache; - '#endif', + if ( arraysEqual( cache, v ) ) return; - '\n' + gl.uniform3uiv( this.addr, v ); - ].filter( filterEmptyLine ).join( '\n' ); + copyArray( cache, v ); - prefixFragment = [ + } - customExtensions, + function setValueV4ui( gl, v ) { - generatePrecision( parameters ), + const cache = this.cache; - '#define SHADER_NAME ' + parameters.shaderName, + if ( arraysEqual( cache, v ) ) return; - customDefines, + gl.uniform4uiv( this.addr, v ); - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer + copyArray( cache, v ); - '#define GAMMA_FACTOR ' + gammaFactorDefine, + } - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + // Single texture (2D / Cube) - parameters.sheen ? '#define USE_SHEEN' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + function setValueT1( gl, v, textures ) { - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', + if ( cache[ 0 ] !== unit ) { - parameters.flatShading ? '#define FLAT_SHADED' : '', + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + textures.setTexture2D( v || emptyTexture, unit ); - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', + } - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', + function setValueT3D1( gl, v, textures ) { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', + if ( cache[ 0 ] !== unit ) { - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', + } - parameters.dithering ? '#define DITHERING' : '', + textures.setTexture3D( v || empty3dTexture, unit ); - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), + } - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', + function setValueT6( gl, v, textures ) { - '\n' + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - ].filter( filterEmptyLine ).join( '\n' ); + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; } - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); + textures.setTextureCube( v || emptyCubeTexture, unit ); - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); + } - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); + function setValueT2DArray1( gl, v, textures ) { - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - // GLSL 3.0 conversion for built-in materials and ShaderMaterial + if ( cache[ 0 ] !== unit ) { - versionString = '#version 300 es\n'; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; + } - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; + textures.setTexture2DArray( v || emptyArrayTexture, unit ); - } + } - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; + // Helper to pick the right setter for the singular case - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); + function getSingularSetter( type ) { - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); + switch ( type ) { - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); + case 0x1406: return setValueV1f; // FLOAT + case 0x8b50: return setValueV2f; // _VEC2 + case 0x8b51: return setValueV3f; // _VEC3 + case 0x8b52: return setValueV4f; // _VEC4 - // Force a particular attribute to index 0. + case 0x8b5a: return setValueM2; // _MAT2 + case 0x8b5b: return setValueM3; // _MAT3 + case 0x8b5c: return setValueM4; // _MAT4 - if ( parameters.index0AttributeName !== undefined ) { + case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); + case 0x1405: return setValueV1ui; // UINT + case 0x8dc6: return setValueV2ui; // _VEC2 + case 0x8dc7: return setValueV3ui; // _VEC3 + case 0x8dc8: return setValueV4ui; // _VEC4 - } else if ( parameters.morphTargets === true ) { + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1; - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3D1; + + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6; + + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArray1; } - gl.linkProgram( program ); + } - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); + // Array of scalars - let runnable = true; - let haveDiagnostics = true; + function setValueV1fArray( gl, v ) { - if ( gl.getProgramParameter( program, 35714 ) === false ) { + gl.uniform1fv( this.addr, v ); - runnable = false; + } - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); + // Array of vectors (from flat array or array of THREE.VectorN) - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); + function setValueV2fArray( gl, v ) { - } else if ( programLog !== '' ) { + const data = flatten( v, this.size, 2 ); - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + gl.uniform2fv( this.addr, data ); - } else if ( vertexLog === '' || fragmentLog === '' ) { + } - haveDiagnostics = false; + function setValueV3fArray( gl, v ) { - } + const data = flatten( v, this.size, 3 ); - if ( haveDiagnostics ) { + gl.uniform3fv( this.addr, data ); - this.diagnostics = { + } - runnable: runnable, + function setValueV4fArray( gl, v ) { - programLog: programLog, + const data = flatten( v, this.size, 4 ); - vertexShader: { + gl.uniform4fv( this.addr, data ); - log: vertexLog, - prefix: prefixVertex + } - }, + // Array of matrices (from flat array or array of THREE.MatrixN) - fragmentShader: { + function setValueM2Array( gl, v ) { - log: fragmentLog, - prefix: prefixFragment + const data = flatten( v, this.size, 4 ); - } + gl.uniformMatrix2fv( this.addr, false, data ); - }; + } - } + function setValueM3Array( gl, v ) { - } + const data = flatten( v, this.size, 9 ); - // Clean up + gl.uniformMatrix3fv( this.addr, false, data ); - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); + } - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); + function setValueM4Array( gl, v ) { - // set up caching for uniform locations + const data = flatten( v, this.size, 16 ); - let cachedUniforms; + gl.uniformMatrix4fv( this.addr, false, data ); - this.getUniforms = function () { + } - if ( cachedUniforms === undefined ) { + // Array of integer / boolean - cachedUniforms = new WebGLUniforms( gl, program ); + function setValueV1iArray( gl, v ) { - } + gl.uniform1iv( this.addr, v ); - return cachedUniforms; + } - }; + // Array of integer / boolean vectors (from flat array) - // set up caching for attribute locations + function setValueV2iArray( gl, v ) { - let cachedAttributes; + gl.uniform2iv( this.addr, v ); - this.getAttributes = function () { + } - if ( cachedAttributes === undefined ) { + function setValueV3iArray( gl, v ) { - cachedAttributes = fetchAttributeLocations( gl, program ); + gl.uniform3iv( this.addr, v ); - } + } - return cachedAttributes; + function setValueV4iArray( gl, v ) { - }; + gl.uniform4iv( this.addr, v ); - // free resource + } - this.destroy = function () { + // Array of unsigned integer - bindingStates.releaseStatesOfProgram( this ); + function setValueV1uiArray( gl, v ) { - gl.deleteProgram( program ); - this.program = undefined; + gl.uniform1uiv( this.addr, v ); - }; + } - // + // Array of unsigned integer vectors (from flat array) - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; + function setValueV2uiArray( gl, v ) { - return this; + gl.uniform2uiv( this.addr, v ); } - function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) { + function setValueV3uiArray( gl, v ) { - const programs = []; + gl.uniform3uiv( this.addr, v ); - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; + } - let precision = capabilities.precision; + function setValueV4uiArray( gl, v ) { - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; + gl.uniform4uiv( this.addr, v ); - const parameterNames = [ - 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', - 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', - 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap', - 'roughnessMap', 'metalnessMap', 'gradientMap', - 'alphaMap', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', - 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', - 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha', - 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', - 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', - 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', - 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', - 'sheen', 'transmissionMap' - ]; + } - function getMaxBones( object ) { - const skeleton = object.skeleton; - const bones = skeleton.bones; + // Array of textures (2D / 3D / Cube / 2DArray) - if ( floatVertexTextures ) { + function setValueT1Array( gl, v, textures ) { - return 1024; + const n = v.length; - } else { + const units = allocTexUnits( textures, n ); - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) + gl.uniform1iv( this.addr, units ); - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + for ( let i = 0; i !== n; ++ i ) { - const maxBones = Math.min( nVertexMatrices, bones.length ); + textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); - if ( maxBones < bones.length ) { + } - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; + } - } + function setValueT3DArray( gl, v, textures ) { - return maxBones; + const n = v.length; - } + const units = allocTexUnits( textures, n ); - } + gl.uniform1iv( this.addr, units ); - function getTextureEncodingFromMap( map ) { + for ( let i = 0; i !== n; ++ i ) { - let encoding; + textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); - if ( map && map.isTexture ) { + } - encoding = map.encoding; + } - } else if ( map && map.isWebGLRenderTarget ) { + function setValueT6Array( gl, v, textures ) { - console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); - encoding = map.texture.encoding; + const n = v.length; - } else { + const units = allocTexUnits( textures, n ); - encoding = LinearEncoding; + gl.uniform1iv( this.addr, units ); - } + for ( let i = 0; i !== n; ++ i ) { - return encoding; + textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } - function getParameters( material, lights, shadows, scene, object ) { + } - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; + function setValueT2DArrayArray( gl, v, textures ) { - const envMap = cubemaps.get( material.envMap || environment ); + const n = v.length; - const shaderID = shaderIDs[ material.type ]; + const units = allocTexUnits( textures, n ); - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + gl.uniform1iv( this.addr, units ); - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; + for ( let i = 0; i !== n; ++ i ) { - if ( material.precision !== null ) { + textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); - precision = capabilities.getMaxPrecision( material.precision ); + } - if ( precision !== material.precision ) { + } - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - } + // Helper to pick the right setter for a pure (bottom-level) array - } + function getPureArraySetter( type ) { - let vertexShader, fragmentShader; + switch ( type ) { - if ( shaderID ) { + case 0x1406: return setValueV1fArray; // FLOAT + case 0x8b50: return setValueV2fArray; // _VEC2 + case 0x8b51: return setValueV3fArray; // _VEC3 + case 0x8b52: return setValueV4fArray; // _VEC4 - const shader = ShaderLib[ shaderID ]; + case 0x8b5a: return setValueM2Array; // _MAT2 + case 0x8b5b: return setValueM3Array; // _MAT3 + case 0x8b5c: return setValueM4Array; // _MAT4 - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; + case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - } else { + case 0x1405: return setValueV1uiArray; // UINT + case 0x8dc6: return setValueV2uiArray; // _VEC2 + case 0x8dc7: return setValueV3uiArray; // _VEC3 + case 0x8dc8: return setValueV4uiArray; // _VEC4 - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1Array; - } + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3DArray; - const currentRenderTarget = renderer.getRenderTarget(); + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6Array; - const parameters = { + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArrayArray; - isWebGL2: isWebGL2, + } - shaderID: shaderID, - shaderName: material.type, + } - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, + // --- Uniform Classes --- - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, + function SingleUniform( id, activeInfo, addr ) { - precision: precision, + this.id = id; + this.addr = addr; + this.cache = []; + this.setValue = getSingularSetter( activeInfo.type ); - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, + // this.path = activeInfo.name; // DEBUG - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, + } - gradientMap: !! material.gradientMap, + function PureArrayUniform( id, activeInfo, addr ) { - sheen: !! material.sheen, + this.id = id; + this.addr = addr; + this.cache = []; + this.size = activeInfo.size; + this.setValue = getPureArraySetter( activeInfo.type ); - transmissionMap: !! material.transmissionMap, + // this.path = activeInfo.name; // DEBUG - combine: material.combine, + } - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexAlphas: material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap, + PureArrayUniform.prototype.updateCache = function ( data ) { - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), + const cache = this.cache; - flatShading: !! material.flatShading, + if ( data instanceof Float32Array && cache.length !== data.length ) { - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, + this.cache = new Float32Array( data.length ); - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, + } - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, + copyArray( cache, data ); - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, + }; - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, + function StructuredUniform( id ) { - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, + this.id = id; - dithering: material.dithering, + this.seq = []; + this.map = {}; - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, + } - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, + StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - premultipliedAlpha: material.premultipliedAlpha, + const seq = this.seq; - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, + const u = seq[ i ]; + u.setValue( gl, value[ u.id ], textures ); - index0AttributeName: material.index0AttributeName, + } - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, + }; - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), + // --- Top-level --- - customProgramCacheKey: material.customProgramCacheKey() + // Parser - builds up the property tree from the path strings - }; + const RePathPart = /(\w+)(\])?(\[|\.)?/g; - return parameters; + // extracts + // - the identifier (member name or array index) + // - followed by an optional right bracket (found when array index) + // - followed by an optional left bracket or dot (type of subscript) + // + // Note: These portions can be read in a non-overlapping fashion and + // allow straightforward parsing of the hierarchy that WebGL encodes + // in the uniform names. - } + function addUniform( container, uniformObject ) { - function getProgramCacheKey( parameters ) { + container.seq.push( uniformObject ); + container.map[ uniformObject.id ] = uniformObject; - const array = []; + } - if ( parameters.shaderID ) { + function parseUniform( activeInfo, addr, container ) { - array.push( parameters.shaderID ); + const path = activeInfo.name, + pathLength = path.length; - } else { + // reset RegExp object, because of the early exit of a previous run + RePathPart.lastIndex = 0; - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); + while ( true ) { - } + const match = RePathPart.exec( path ), + matchEnd = RePathPart.lastIndex; - if ( parameters.defines !== undefined ) { + let id = match[ 1 ]; + const idIsIndex = match[ 2 ] === ']', + subscript = match[ 3 ]; - for ( const name in parameters.defines ) { + if ( idIsIndex ) id = id | 0; // convert to integer - array.push( name ); - array.push( parameters.defines[ name ] ); + if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - } + // bare name or "pure" bottom-level array "[0]" suffix - } + addUniform( container, subscript === undefined ? + new SingleUniform( id, activeInfo, addr ) : + new PureArrayUniform( id, activeInfo, addr ) ); - if ( parameters.isRawShaderMaterial === false ) { + break; - for ( let i = 0; i < parameterNames.length; i ++ ) { + } else { - array.push( parameters[ parameterNames[ i ] ] ); + // step into inner node / create it in case it doesn't exist - } + const map = container.map; + let next = map[ id ]; - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); + if ( next === undefined ) { - } + next = new StructuredUniform( id ); + addUniform( container, next ); - array.push( parameters.customProgramCacheKey ); + } - return array.join(); + container = next; + + } } - function getUniforms( material ) { + } - const shaderID = shaderIDs[ material.type ]; - let uniforms; + // Root Container - if ( shaderID ) { + function WebGLUniforms( gl, program ) { - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); + this.seq = []; + this.map = {}; - } else { + const n = gl.getProgramParameter( program, 35718 ); - uniforms = material.uniforms; + for ( let i = 0; i < n; ++ i ) { - } + const info = gl.getActiveUniform( program, i ), + addr = gl.getUniformLocation( program, info.name ); - return uniforms; + parseUniform( info, addr, this ); } - function acquireProgram( parameters, cacheKey ) { - - let program; - - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { + } - const preexistingProgram = programs[ p ]; + WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - if ( preexistingProgram.cacheKey === cacheKey ) { + const u = this.map[ name ]; - program = preexistingProgram; - ++ program.usedTimes; + if ( u !== undefined ) u.setValue( gl, value, textures ); - break; + }; - } + WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - } + const v = object[ name ]; - if ( program === undefined ) { + if ( v !== undefined ) this.setValue( gl, name, v ); - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); + }; - } - return program; + // Static interface - } + WebGLUniforms.upload = function ( gl, seq, values, textures ) { - function releaseProgram( program ) { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - if ( -- program.usedTimes === 0 ) { + const u = seq[ i ], + v = values[ u.id ]; - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); + if ( v.needsUpdate !== false ) { - // Free WebGL resources - program.destroy(); + // note: always updating when .needsUpdate is undefined + u.setValue( gl, v.value, textures ); } } - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; + }; - } + WebGLUniforms.seqWithValue = function ( seq, values ) { - function WebGLProperties() { + const r = []; - let properties = new WeakMap(); + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - function get( object ) { + const u = seq[ i ]; + if ( u.id in values ) r.push( u ); - let map = properties.get( object ); + } - if ( map === undefined ) { + return r; - map = {}; - properties.set( object, map ); + }; - } + function WebGLShader( gl, type, string ) { - return map; + const shader = gl.createShader( type ); - } + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - function remove( object ) { + return shader; - properties.delete( object ); + } - } + let programIdCount = 0; - function update( object, key, value ) { + function handleSource( string, errorLine ) { - properties.get( object )[ key ] = value; + const lines = string.split( '\n' ); + const lines2 = []; - } + const from = Math.max( errorLine - 6, 0 ); + const to = Math.min( errorLine + 6, lines.length ); - function dispose() { + for ( let i = from; i < to; i ++ ) { - properties = new WeakMap(); + lines2.push( ( i + 1 ) + ': ' + lines[ i ] ); } - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; + return lines2.join( '\n' ); } - function painterSortStable( a, b ) { + function getEncodingComponents( encoding ) { - if ( a.groupOrder !== b.groupOrder ) { + switch ( encoding ) { - return a.groupOrder - b.groupOrder; + case LinearEncoding: + return [ 'Linear', '( value )' ]; + case sRGBEncoding: + return [ 'sRGB', '( value )' ]; + default: + console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); + return [ 'Linear', '( value )' ]; - } else if ( a.renderOrder !== b.renderOrder ) { + } - return a.renderOrder - b.renderOrder; + } - } else if ( a.program !== b.program ) { + function getShaderErrors( gl, shader, type ) { - return a.program.id - b.program.id; + const status = gl.getShaderParameter( shader, 35713 ); + const errors = gl.getShaderInfoLog( shader ).trim(); - } else if ( a.material.id !== b.material.id ) { + if ( status && errors === '' ) return ''; - return a.material.id - b.material.id; + const errorLine = parseInt( /ERROR: 0:(\d+)/.exec( errors )[ 1 ] ); - } else if ( a.z !== b.z ) { + // --enable-privileged-webgl-extension + // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - return a.z - b.z; + return type.toUpperCase() + '\n\n' + errors + '\n\n' + handleSource( gl.getShaderSource( shader ), errorLine ); - } else { + } - return a.id - b.id; + function getTexelEncodingFunction( functionName, encoding ) { - } + const components = getEncodingComponents( encoding ); + return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; } - function reversePainterSortStable( a, b ) { + function getToneMappingFunction( functionName, toneMapping ) { - if ( a.groupOrder !== b.groupOrder ) { + let toneMappingName; - return a.groupOrder - b.groupOrder; + switch ( toneMapping ) { - } else if ( a.renderOrder !== b.renderOrder ) { + case LinearToneMapping: + toneMappingName = 'Linear'; + break; - return a.renderOrder - b.renderOrder; + case ReinhardToneMapping: + toneMappingName = 'Reinhard'; + break; - } else if ( a.z !== b.z ) { + case CineonToneMapping: + toneMappingName = 'OptimizedCineon'; + break; - return b.z - a.z; + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; - } else { + case CustomToneMapping: + toneMappingName = 'Custom'; + break; - return a.id - b.id; + default: + console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); + toneMappingName = 'Linear'; } + return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + } + function generateExtensions( parameters ) { + + const chunks = [ + ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', + ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' + ]; - function WebGLRenderList( properties ) { + return chunks.filter( filterEmptyLine ).join( '\n' ); - const renderItems = []; - let renderItemsIndex = 0; + } - const opaque = []; - const transparent = []; + function generateDefines( defines ) { - const defaultProgram = { id: - 1 }; + const chunks = []; - function init() { + for ( const name in defines ) { - renderItemsIndex = 0; + const value = defines[ name ]; - opaque.length = 0; - transparent.length = 0; + if ( value === false ) continue; + + chunks.push( '#define ' + name + ' ' + value ); } - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { + return chunks.join( '\n' ); - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); + } - if ( renderItem === undefined ) { + function fetchAttributeLocations( gl, program ) { - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; + const attributes = {}; - renderItems[ renderItemsIndex ] = renderItem; + const n = gl.getProgramParameter( program, 35721 ); - } else { + for ( let i = 0; i < n; i ++ ) { - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; + const info = gl.getActiveAttrib( program, i ); + const name = info.name; - } + let locationSize = 1; + if ( info.type === 35674 ) locationSize = 2; + if ( info.type === 35675 ) locationSize = 3; + if ( info.type === 35676 ) locationSize = 4; - renderItemsIndex ++; + // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - return renderItem; + attributes[ name ] = { + type: info.type, + location: gl.getAttribLocation( program, name ), + locationSize: locationSize + }; } - function push( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + return attributes; - ( material.transparent === true ? transparent : opaque ).push( renderItem ); + } - } + function filterEmptyLine( string ) { - function unshift( object, geometry, material, groupOrder, z, group ) { + return string !== ''; - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + } - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); + function replaceLightNums( string, parameters ) { - } + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) + .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) + .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) + .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - function sort( customOpaqueSort, customTransparentSort ) { + } - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); + function replaceClippingPlaneNums( string, parameters ) { - } + return string + .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) + .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - function finish() { + } - // Clear references from inactive renderItems in the list + // Resolve Includes - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { + const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - const renderItem = renderItems[ i ]; + function resolveIncludes( string ) { - if ( renderItem.id === null ) break; + return string.replace( includePattern, includeReplacer ); - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; + } - } + function includeReplacer( match, include ) { - } + const string = ShaderChunk[ include ]; - return { + if ( string === undefined ) { - opaque: opaque, - transparent: transparent, + throw new Error( 'Can not resolve #include <' + include + '>' ); - init: init, - push: push, - unshift: unshift, - finish: finish, + } - sort: sort - }; + return resolveIncludes( string ); } - function WebGLRenderLists( properties ) { - - let lists = new WeakMap(); - - function get( scene, renderCallDepth ) { + // Unroll Loops - let list; + const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; + const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - if ( lists.has( scene ) === false ) { + function unrollLoops( string ) { - list = new WebGLRenderList( properties ); - lists.set( scene, [ list ] ); + return string + .replace( unrollLoopPattern, loopReplacer ) + .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - } else { + } - if ( renderCallDepth >= lists.get( scene ).length ) { + function deprecatedLoopReplacer( match, start, end, snippet ) { - list = new WebGLRenderList( properties ); - lists.get( scene ).push( list ); + console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); + return loopReplacer( match, start, end, snippet ); - } else { + } - list = lists.get( scene )[ renderCallDepth ]; + function loopReplacer( match, start, end, snippet ) { - } + let string = ''; - } + for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - return list; + string += snippet + .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) + .replace( /UNROLLED_LOOP_INDEX/g, i ); } - function dispose() { + return string; - lists = new WeakMap(); + } - } + // - return { - get: get, - dispose: dispose - }; + function generatePrecision( parameters ) { - } + let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; - function UniformsCache() { + if ( parameters.precision === 'highp' ) { - const lights = {}; + precisionstring += '\n#define HIGH_PRECISION'; - return { + } else if ( parameters.precision === 'mediump' ) { - get: function ( light ) { + precisionstring += '\n#define MEDIUM_PRECISION'; - if ( lights[ light.id ] !== undefined ) { + } else if ( parameters.precision === 'lowp' ) { - return lights[ light.id ]; + precisionstring += '\n#define LOW_PRECISION'; - } + } - let uniforms; + return precisionstring; - switch ( light.type ) { + } - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; + function generateShadowMapTypeDefine( parameters ) { - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; + let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; + if ( parameters.shadowMapType === PCFShadowMap ) { - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; + } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - } + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - lights[ light.id ] = uniforms; + } else if ( parameters.shadowMapType === VSMShadowMap ) { - return uniforms; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - } + } - }; + return shadowMapTypeDefine; } - function ShadowUniformsCache() { + function generateEnvMapTypeDefine( parameters ) { - const lights = {}; + let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - return { + if ( parameters.envMap ) { - get: function ( light ) { + switch ( parameters.envMapMode ) { - if ( lights[ light.id ] !== undefined ) { + case CubeReflectionMapping: + case CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; - return lights[ light.id ]; + case CubeUVReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; - } + } - let uniforms; + } - switch ( light.type ) { + return envMapTypeDefine; - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + } - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + function generateEnvMapModeDefine( parameters ) { - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; + let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - // TODO (abelnation): set RectAreaLight shadow uniforms + if ( parameters.envMap ) { - } + switch ( parameters.envMapMode ) { - lights[ light.id ] = uniforms; + case CubeRefractionMapping: - return uniforms; + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; } - }; - - } - - - - let nextVersion = 0; - - function shadowCastingLightsFirst( lightA, lightB ) { + } - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); + return envMapModeDefine; } - function WebGLLights( extensions, capabilities ) { - - const cache = new UniformsCache(); + function generateEnvMapBlendingDefine( parameters ) { - const shadowCache = ShadowUniformsCache(); + let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - const state = { + if ( parameters.envMap ) { - version: 0, + switch ( parameters.combine ) { - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, + case MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, + case MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] + case AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; - }; + } - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); + } - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); + return envMapBlendingDefine; - function setup( lights ) { + } - let r = 0, g = 0, b = 0; + function generateCubeUVSize( parameters ) { - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); + const imageHeight = parameters.envMapCubeUVHeight; - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + if ( imageHeight === null ) return null; - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; + const maxMip = Math.log2( imageHeight / 32 + 1 ) + 3; - lights.sort( shadowCastingLightsFirst ); + const texelHeight = 1.0 / imageHeight; - for ( let i = 0, l = lights.length; i < l; i ++ ) { + const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) ); - const light = lights[ i ]; + return { texelWidth, texelHeight, maxMip }; - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; + } - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - if ( light.isAmbientLight ) { + // TODO Send this event to Three.js DevTools + // console.log( 'WebGLProgram', cacheKey ); - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; + const gl = renderer.getContext(); - } else if ( light.isLightProbe ) { + const defines = parameters.defines; - for ( let j = 0; j < 9; j ++ ) { + let vertexShader = parameters.vertexShader; + let fragmentShader = parameters.fragmentShader; - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); + const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); + const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); + const envMapModeDefine = generateEnvMapModeDefine( parameters ); + const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + const envMapCubeUVSize = generateCubeUVSize( parameters ); - } + const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - } else if ( light.isDirectionalLight ) { + const customDefines = generateDefines( defines ); - const uniforms = cache.get( light ); + const program = gl.createProgram(); - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + let prefixVertex, prefixFragment; + let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; - if ( light.castShadow ) { + if ( parameters.isRawShaderMaterial ) { - const shadow = light.shadow; + prefixVertex = [ - const shadowUniforms = shadowCache.get( light ); + customDefines - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + ].filter( filterEmptyLine ).join( '\n' ); - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + if ( prefixVertex.length > 0 ) { - numDirectionalShadows ++; + prefixVertex += '\n'; - } + } - state.directional[ directionalLength ] = uniforms; + prefixFragment = [ - directionalLength ++; + customExtensions, + customDefines - } else if ( light.isSpotLight ) { + ].filter( filterEmptyLine ).join( '\n' ); - const uniforms = cache.get( light ); + if ( prefixFragment.length > 0 ) { - uniforms.position.setFromMatrixPosition( light.matrixWorld ); + prefixFragment += '\n'; - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; + } - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; + } else { - if ( light.castShadow ) { + prefixVertex = [ - const shadow = light.shadow; + generatePrecision( parameters ), - const shadowUniforms = shadowCache.get( light ); + '#define SHADER_NAME ' + parameters.shaderName, - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + customDefines, - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + parameters.instancing ? '#define USE_INSTANCING' : '', + parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - numSpotShadows ++; + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - } + '#define MAX_BONES ' + parameters.maxBones, + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - state.spot[ spotLength ] = uniforms; + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - spotLength ++; + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - } else if ( light.isRectAreaLight ) { + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - const uniforms = cache.get( light ); + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - state.rectArea[ rectAreaLength ] = uniforms; + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - rectAreaLength ++; + parameters.flatShading ? '#define FLAT_SHADED' : '', - } else if ( light.isPointLight ) { + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - const uniforms = cache.get( light ); + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + ( parameters.morphColors && parameters.isWebGL2 ) ? '#define USE_MORPHCOLORS' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - if ( light.castShadow ) { + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - const shadow = light.shadow; + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - const shadowUniforms = shadowCache.get( light ); + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; + '#ifdef USE_INSTANCING', - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; + ' attribute mat4 instanceMatrix;', - numPointShadows ++; + '#endif', - } + '#ifdef USE_INSTANCING_COLOR', - state.point[ pointLength ] = uniforms; + ' attribute vec3 instanceColor;', - pointLength ++; + '#endif', - } else if ( light.isHemisphereLight ) { + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - const uniforms = cache.get( light ); + '#ifdef USE_TANGENT', - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); + ' attribute vec4 tangent;', - state.hemi[ hemiLength ] = uniforms; + '#endif', - hemiLength ++; + '#if defined( USE_COLOR_ALPHA )', - } + ' attribute vec4 color;', - } + '#elif defined( USE_COLOR )', - if ( rectAreaLength > 0 ) { + ' attribute vec3 color;', - if ( capabilities.isWebGL2 ) { + '#endif', - // WebGL 2 + '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )', - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - } else { + ' #ifdef USE_MORPHNORMALS', - // WebGL 1 + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - if ( extensions.has( 'OES_texture_float_linear' ) === true ) { + ' #else', - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { + ' #endif', - state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; - state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; + '#endif', - } else { + '#ifdef USE_SKINNING', - console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - } + '#endif', - } + '\n' - } + ].filter( filterEmptyLine ).join( '\n' ); - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; + prefixFragment = [ - const hash = state.hash; + customExtensions, - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { + generatePrecision( parameters ), - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; + '#define SHADER_NAME ' + parameters.shaderName, - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; + customDefines, - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; + parameters.map ? '#define USE_MAP' : '', + parameters.matcap ? '#define USE_MATCAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '', + envMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - state.version = nextVersion ++; + parameters.clearcoat ? '#define USE_CLEARCOAT' : '', + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - } + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - } + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.alphaTest ? '#define USE_ALPHATEST' : '', - function setupView( lights, camera ) { + parameters.sheen ? '#define USE_SHEEN' : '', + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - const viewMatrix = camera.matrixWorldInverse; + parameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '', - for ( let i = 0, l = lights.length; i < l; i ++ ) { + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - const light = lights[ i ]; + parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - if ( light.isDirectionalLight ) { + parameters.flatShading ? '#define FLAT_SHADED' : '', - const uniforms = state.directional[ directionalLength ]; + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - directionalLength ++; + parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - } else if ( light.isSpotLight ) { + parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - const uniforms = state.spot[ spotLength ]; + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', + ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below + ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - spotLength ++; + parameters.dithering ? '#define DITHERING' : '', + parameters.opaque ? '#define OPAQUE' : '', - } else if ( light.isRectAreaLight ) { + ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below + getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - const uniforms = state.rectArea[ rectAreaLength ]; + parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + '\n' - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); + ].filter( filterEmptyLine ).join( '\n' ); - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + } - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); + vertexShader = resolveIncludes( vertexShader ); + vertexShader = replaceLightNums( vertexShader, parameters ); + vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - rectAreaLength ++; + fragmentShader = resolveIncludes( fragmentShader ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); + fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - } else if ( light.isPointLight ) { + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); - const uniforms = state.point[ pointLength ]; + if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + // GLSL 3.0 conversion for built-in materials and ShaderMaterial - pointLength ++; + versionString = '#version 300 es\n'; - } else if ( light.isHemisphereLight ) { + prefixVertex = [ + 'precision mediump sampler2DArray;', + '#define attribute in', + '#define varying out', + '#define texture2D texture' + ].join( '\n' ) + '\n' + prefixVertex; - const uniforms = state.hemi[ hemiLength ]; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - hemiLength ++; - - } - - } + prefixFragment = [ + '#define varying in', + ( parameters.glslVersion === GLSL3 ) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;', + ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', + '#define gl_FragDepthEXT gl_FragDepth', + '#define texture2D texture', + '#define textureCube texture', + '#define texture2DProj textureProj', + '#define texture2DLodEXT textureLod', + '#define texture2DProjLodEXT textureProjLod', + '#define textureCubeLodEXT textureLod', + '#define texture2DGradEXT textureGrad', + '#define texture2DProjGradEXT textureProjGrad', + '#define textureCubeGradEXT textureGrad' + ].join( '\n' ) + '\n' + prefixFragment; } - return { - setup: setup, - setupView: setupView, - state: state - }; - - } + const vertexGlsl = versionString + prefixVertex + vertexShader; + const fragmentGlsl = versionString + prefixFragment + fragmentShader; - function WebGLRenderState( extensions, capabilities ) { + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); - const lights = new WebGLLights( extensions, capabilities ); + const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); + const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - const lightsArray = []; - const shadowsArray = []; + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); - function init() { + // Force a particular attribute to index 0. - lightsArray.length = 0; - shadowsArray.length = 0; + if ( parameters.index0AttributeName !== undefined ) { - } + gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - function pushLight( light ) { + } else if ( parameters.morphTargets === true ) { - lightsArray.push( light ); + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); } - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } + gl.linkProgram( program ); - function setupLights() { + // check for link errors + if ( renderer.debug.checkShaderErrors ) { - lights.setup( lightsArray ); + const programLog = gl.getProgramInfoLog( program ).trim(); + const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); + const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - } + let runnable = true; + let haveDiagnostics = true; - function setupLightsView( camera ) { + if ( gl.getProgramParameter( program, 35714 ) === false ) { - lights.setupView( lightsArray, camera ); + runnable = false; - } + const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); + const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, + console.error( + 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + + 'VALIDATE_STATUS ' + gl.getProgramParameter( program, 35715 ) + '\n\n' + + 'Program Info Log: ' + programLog + '\n' + + vertexErrors + '\n' + + fragmentErrors + ); - lights: lights - }; + } else if ( programLog !== '' ) { - return { - init: init, - state: state, - setupLights: setupLights, - setupLightsView: setupLightsView, + console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); - pushLight: pushLight, - pushShadow: pushShadow - }; + } else if ( vertexLog === '' || fragmentLog === '' ) { - } + haveDiagnostics = false; - function WebGLRenderStates( extensions, capabilities ) { + } - let renderStates = new WeakMap(); + if ( haveDiagnostics ) { - function get( scene, renderCallDepth = 0 ) { + this.diagnostics = { - let renderState; + runnable: runnable, - if ( renderStates.has( scene ) === false ) { + programLog: programLog, - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.set( scene, [ renderState ] ); + vertexShader: { - } else { + log: vertexLog, + prefix: prefixVertex - if ( renderCallDepth >= renderStates.get( scene ).length ) { + }, - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.get( scene ).push( renderState ); + fragmentShader: { - } else { + log: fragmentLog, + prefix: prefixFragment - renderState = renderStates.get( scene )[ renderCallDepth ]; + } - } + }; } - return renderState; - } - function dispose() { + // Clean up - renderStates = new WeakMap(); + // Crashes in iOS9 and iOS10. #18402 + // gl.detachShader( program, glVertexShader ); + // gl.detachShader( program, glFragmentShader ); - } + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - return { - get: get, - dispose: dispose - }; + // set up caching for uniform locations - } + let cachedUniforms; - /** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + this.getUniforms = function () { - class MeshDepthMaterial extends Material { + if ( cachedUniforms === undefined ) { - constructor( parameters ) { + cachedUniforms = new WebGLUniforms( gl, program ); - super(); + } - this.type = 'MeshDepthMaterial'; + return cachedUniforms; - this.depthPacking = BasicDepthPacking; + }; - this.skinning = false; - this.morphTargets = false; + // set up caching for attribute locations - this.map = null; + let cachedAttributes; - this.alphaMap = null; + this.getAttributes = function () { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( cachedAttributes === undefined ) { - this.wireframe = false; - this.wireframeLinewidth = 1; + cachedAttributes = fetchAttributeLocations( gl, program ); - this.fog = false; + } - this.setValues( parameters ); + return cachedAttributes; - } + }; - copy( source ) { + // free resource - super.copy( source ); + this.destroy = function () { - this.depthPacking = source.depthPacking; + bindingStates.releaseStatesOfProgram( this ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + gl.deleteProgram( program ); + this.program = undefined; - this.map = source.map; + }; - this.alphaMap = source.alphaMap; + // - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.name = parameters.shaderName; + this.id = programIdCount ++; + this.cacheKey = cacheKey; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + return this; - return this; + } - } + let _id = 0; - } + class WebGLShaderCache { - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; + constructor() { - /** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ + this.shaderCache = new Map(); + this.materialCache = new Map(); - class MeshDistanceMaterial extends Material { + } - constructor( parameters ) { + update( material ) { - super(); + const vertexShader = material.vertexShader; + const fragmentShader = material.fragmentShader; - this.type = 'MeshDistanceMaterial'; + const vertexShaderStage = this._getShaderStage( vertexShader ); + const fragmentShaderStage = this._getShaderStage( fragmentShader ); - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; + const materialShaders = this._getShaderCacheForMaterial( material ); - this.skinning = false; - this.morphTargets = false; + if ( materialShaders.has( vertexShaderStage ) === false ) { - this.map = null; + materialShaders.add( vertexShaderStage ); + vertexShaderStage.usedTimes ++; - this.alphaMap = null; + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( materialShaders.has( fragmentShaderStage ) === false ) { - this.fog = false; + materialShaders.add( fragmentShaderStage ); + fragmentShaderStage.usedTimes ++; - this.setValues( parameters ); + } + + return this; } - copy( source ) { + remove( material ) { - super.copy( source ); + const materialShaders = this.materialCache.get( material ); - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; + for ( const shaderStage of materialShaders ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + shaderStage.usedTimes --; - this.map = source.map; + if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); - this.alphaMap = source.alphaMap; + } - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.materialCache.delete( material ); return this; } - } - - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; + getVertexShaderID( material ) { - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; + return this._getShaderStage( material.vertexShader ).id; - function WebGLShadowMap( _renderer, _objects, _capabilities ) { + } - let _frustum = new Frustum(); + getFragmentShaderID( material ) { - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), + return this._getShaderStage( material.fragmentShader ).id; - _viewport = new Vector4(), + } - _depthMaterials = [], - _distanceMaterials = [], + dispose() { - _materialCache = {}, + this.shaderCache.clear(); + this.materialCache.clear(); - _maxTextureSize = _capabilities.maxTextureSize; + } - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; + _getShaderCacheForMaterial( material ) { - const shadowMaterialVertical = new ShaderMaterial( { + const cache = this.materialCache; - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, + if ( cache.has( material ) === false ) { - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, + cache.set( material, new Set() ); - vertexShader: vsm_vert, + } - fragmentShader: vsm_frag + return cache.get( material ); - } ); + } - const shadowMaterialHorizontal = shadowMaterialVertical.clone(); - shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; + _getShaderStage( code ) { - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - 'position', - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); + const cache = this.shaderCache; - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); + if ( cache.has( code ) === false ) { - const scope = this; + const stage = new WebGLShaderStage( code ); + cache.set( code, stage ); - this.enabled = false; + } - this.autoUpdate = true; - this.needsUpdate = false; + return cache.get( code ); - this.type = PCFShadowMap; + } - this.render = function ( lights, scene, camera ) { + } - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + class WebGLShaderStage { - if ( lights.length === 0 ) return; + constructor( code ) { - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); + this.id = _id ++; - const _state = _renderer.state; + this.code = code; + this.usedTimes = 0; - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); + } - // render depth map + } - for ( let i = 0, il = lights.length; i < il; i ++ ) { + function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { - const light = lights[ i ]; - const shadow = light.shadow; + const _programLayers = new Layers(); + const _customShaders = new WebGLShaderCache(); + const programs = []; - if ( shadow === undefined ) { + const isWebGL2 = capabilities.isWebGL2; + const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; + const floatVertexTextures = capabilities.floatVertexTextures; + const maxVertexUniforms = capabilities.maxVertexUniforms; + const vertexTextures = capabilities.vertexTextures; + let precision = capabilities.precision; - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; + const shaderIDs = { + MeshDepthMaterial: 'depth', + MeshDistanceMaterial: 'distanceRGBA', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshToonMaterial: 'toon', + MeshStandardMaterial: 'physical', + MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points', + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' + }; - } + function getMaxBones( object ) { - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; + const skeleton = object.skeleton; + const bones = skeleton.bones; - _shadowMapSize.copy( shadow.mapSize ); + if ( floatVertexTextures ) { - const shadowFrameExtents = shadow.getFrameExtents(); + return 1024; - _shadowMapSize.multiply( shadowFrameExtents ); + } else { - _viewportSize.copy( shadow.mapSize ); + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) - if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { + const nVertexUniforms = maxVertexUniforms; + const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - if ( _shadowMapSize.x > _maxTextureSize ) { + const maxBones = Math.min( nVertexMatrices, bones.length ); - _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; + if ( maxBones < bones.length ) { - } + console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); + return 0; - if ( _shadowMapSize.y > _maxTextureSize ) { + } - _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; + return maxBones; - } + } - } + } - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + function getParameters( material, lights, shadows, scene, object ) { - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; + const fog = scene.fog; + const geometry = object.geometry; + const environment = material.isMeshStandardMaterial ? scene.environment : null; - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + const shaderID = shaderIDs[ material.type ]; - shadow.camera.updateProjectionMatrix(); + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) - } + const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - if ( shadow.map === null ) { + if ( material.precision !== null ) { - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; + precision = capabilities.getMaxPrecision( material.precision ); - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + if ( precision !== material.precision ) { - shadow.camera.updateProjectionMatrix(); + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - const viewportCount = shadow.getViewportCount(); + } - for ( let vp = 0; vp < viewportCount; vp ++ ) { + // - const viewport = shadow.getViewport( vp ); + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); + let morphTextureStride = 0; - _state.viewport( _viewport ); + if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; + if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; + if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; - shadow.updateMatrices( light, vp ); + // - _frustum = shadow.getFrustum(); + let vertexShader, fragmentShader; + let customVertexShaderID, customFragmentShaderID; - renderObject( scene, camera, shadow.camera, light, this.type ); + if ( shaderID ) { - } + const shader = ShaderLib[ shaderID ]; - // do blur pass for VSM + vertexShader = shader.vertexShader; + fragmentShader = shader.fragmentShader; - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + } else { - VSMPass( shadow, camera ); + vertexShader = material.vertexShader; + fragmentShader = material.fragmentShader; - } + _customShaders.update( material ); - shadow.needsUpdate = false; + customVertexShaderID = _customShaders.getVertexShaderID( material ); + customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } - scope.needsUpdate = false; + const currentRenderTarget = renderer.getRenderTarget(); - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); + const useAlphaTest = material.alphaTest > 0; + const useClearcoat = material.clearcoat > 0; - }; + const parameters = { - function VSMPass( shadow, camera ) { + isWebGL2: isWebGL2, - const geometry = _objects.update( fullScreenMesh ); + shaderID: shaderID, + shaderName: material.type, - // vertical pass + vertexShader: vertexShader, + fragmentShader: fragmentShader, + defines: material.defines, - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); + customVertexShaderID: customVertexShaderID, + customFragmentShaderID: customFragmentShaderID, - // horizontal pass + isRawShaderMaterial: material.isRawShaderMaterial === true, + glslVersion: material.glslVersion, - shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); + precision: precision, - } + instancing: object.isInstancedMesh === true, + instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { + supportsVertexTextures: vertexTextures, + outputEncoding: ( currentRenderTarget === null ) ? renderer.outputEncoding : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding ), + map: !! material.map, + matcap: !! material.matcap, + envMap: !! envMap, + envMapMode: envMap && envMap.mapping, + envMapCubeUVHeight: envMapCubeUVHeight, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, + tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + decodeVideoTexture: !! material.map && ( material.map.isVideoTexture === true ) && ( material.map.encoding === sRGBEncoding ), - let material = _depthMaterials[ index ]; + clearcoat: useClearcoat, + clearcoatMap: useClearcoat && !! material.clearcoatMap, + clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, + clearcoatNormalMap: useClearcoat && !! material.clearcoatNormalMap, - if ( material === undefined ) { + displacementMap: !! material.displacementMap, + roughnessMap: !! material.roughnessMap, + metalnessMap: !! material.metalnessMap, + specularMap: !! material.specularMap, + specularIntensityMap: !! material.specularIntensityMap, + specularColorMap: !! material.specularColorMap, - material = new MeshDepthMaterial( { + opaque: material.transparent === false && material.blending === NormalBlending, - depthPacking: RGBADepthPacking, + alphaMap: !! material.alphaMap, + alphaTest: useAlphaTest, - morphTargets: useMorphing, - skinning: useSkinning + gradientMap: !! material.gradientMap, - } ); + sheen: material.sheen > 0, + sheenColorMap: !! material.sheenColorMap, + sheenRoughnessMap: !! material.sheenRoughnessMap, - _depthMaterials[ index ] = material; + transmission: material.transmission > 0, + transmissionMap: !! material.transmissionMap, + thicknessMap: !! material.thicknessMap, - } + combine: material.combine, - return material; + vertexTangents: ( !! material.normalMap && !! geometry.attributes.tangent ), + vertexColors: material.vertexColors, + vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, + vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || !! material.sheenColorMap || !! material.sheenRoughnessMap, + uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || material.transmission > 0 || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || material.sheen > 0 || !! material.sheenColorMap || !! material.sheenRoughnessMap ) && !! material.displacementMap, - } + fog: !! fog, + useFog: material.fog, + fogExp2: ( fog && fog.isFogExp2 ), - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { + flatShading: !! material.flatShading, - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: logarithmicDepthBuffer, - let material = _distanceMaterials[ index ]; + skinning: object.isSkinnedMesh === true && maxBones > 0, + maxBones: maxBones, + useVertexTexture: floatVertexTextures, - if ( material === undefined ) { + morphTargets: geometry.morphAttributes.position !== undefined, + morphNormals: geometry.morphAttributes.normal !== undefined, + morphColors: geometry.morphAttributes.color !== undefined, + morphTargetsCount: morphTargetsCount, + morphTextureStride: morphTextureStride, - material = new MeshDistanceMaterial( { + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numRectAreaLights: lights.rectArea.length, + numHemiLights: lights.hemi.length, - morphTargets: useMorphing, - skinning: useSkinning + numDirLightShadows: lights.directionalShadowMap.length, + numPointLightShadows: lights.pointShadowMap.length, + numSpotLightShadows: lights.spotShadowMap.length, - } ); + numClippingPlanes: clipping.numPlanes, + numClipIntersection: clipping.numIntersection, - _distanceMaterials[ index ] = material; + dithering: material.dithering, - } + shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, + shadowMapType: renderer.shadowMap.type, - return material; + toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, + physicallyCorrectLights: renderer.physicallyCorrectLights, - } + premultipliedAlpha: material.premultipliedAlpha, + + doubleSided: material.side === DoubleSide, + flipSided: material.side === BackSide, - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { + depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - let result = null; + index0AttributeName: material.index0AttributeName, - let getMaterialVariant = getDepthMaterialVariant; - let customMaterial = object.customDepthMaterial; + extensionDerivatives: material.extensions && material.extensions.derivatives, + extensionFragDepth: material.extensions && material.extensions.fragDepth, + extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, + extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - if ( light.isPointLight === true ) { + rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), + rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), + rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; + customProgramCacheKey: material.customProgramCacheKey() - } + }; - if ( customMaterial === undefined ) { + return parameters; - let useMorphing = false; + } - if ( material.morphTargets === true ) { + function getProgramCacheKey( parameters ) { - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; + const array = []; - } + if ( parameters.shaderID ) { - let useSkinning = false; + array.push( parameters.shaderID ); - if ( object.isSkinnedMesh === true ) { + } else { - if ( material.skinning === true ) { + array.push( parameters.customVertexShaderID ); + array.push( parameters.customFragmentShaderID ); - useSkinning = true; + } - } else { + if ( parameters.defines !== undefined ) { - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); + for ( const name in parameters.defines ) { - } + array.push( name ); + array.push( parameters.defines[ name ] ); } - const useInstancing = object.isInstancedMesh === true; - - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); + } - } else { + if ( parameters.isRawShaderMaterial === false ) { - result = customMaterial; + getProgramCacheKeyParameters( array, parameters ); + getProgramCacheKeyBooleans( array, parameters ); + array.push( renderer.outputEncoding ); } - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state + array.push( parameters.customProgramCacheKey ); - const keyA = result.uuid, keyB = material.uuid; + return array.join(); - let materialsForVariant = _materialCache[ keyA ]; + } - if ( materialsForVariant === undefined ) { + function getProgramCacheKeyParameters( array, parameters ) { + + array.push( parameters.precision ); + array.push( parameters.outputEncoding ); + array.push( parameters.envMapMode ); + array.push( parameters.envMapCubeUVHeight ); + array.push( parameters.combine ); + array.push( parameters.vertexUvs ); + array.push( parameters.fogExp2 ); + array.push( parameters.sizeAttenuation ); + array.push( parameters.maxBones ); + array.push( parameters.morphTargetsCount ); + array.push( parameters.morphAttributeCount ); + array.push( parameters.numDirLights ); + array.push( parameters.numPointLights ); + array.push( parameters.numSpotLights ); + array.push( parameters.numHemiLights ); + array.push( parameters.numRectAreaLights ); + array.push( parameters.numDirLightShadows ); + array.push( parameters.numPointLightShadows ); + array.push( parameters.numSpotLightShadows ); + array.push( parameters.shadowMapType ); + array.push( parameters.toneMapping ); + array.push( parameters.numClippingPlanes ); + array.push( parameters.numClipIntersection ); + + } + + function getProgramCacheKeyBooleans( array, parameters ) { + + _programLayers.disableAll(); + + if ( parameters.isWebGL2 ) + _programLayers.enable( 0 ); + if ( parameters.supportsVertexTextures ) + _programLayers.enable( 1 ); + if ( parameters.instancing ) + _programLayers.enable( 2 ); + if ( parameters.instancingColor ) + _programLayers.enable( 3 ); + if ( parameters.map ) + _programLayers.enable( 4 ); + if ( parameters.matcap ) + _programLayers.enable( 5 ); + if ( parameters.envMap ) + _programLayers.enable( 6 ); + if ( parameters.lightMap ) + _programLayers.enable( 7 ); + if ( parameters.aoMap ) + _programLayers.enable( 8 ); + if ( parameters.emissiveMap ) + _programLayers.enable( 9 ); + if ( parameters.bumpMap ) + _programLayers.enable( 10 ); + if ( parameters.normalMap ) + _programLayers.enable( 11 ); + if ( parameters.objectSpaceNormalMap ) + _programLayers.enable( 12 ); + if ( parameters.tangentSpaceNormalMap ) + _programLayers.enable( 13 ); + if ( parameters.clearcoat ) + _programLayers.enable( 14 ); + if ( parameters.clearcoatMap ) + _programLayers.enable( 15 ); + if ( parameters.clearcoatRoughnessMap ) + _programLayers.enable( 16 ); + if ( parameters.clearcoatNormalMap ) + _programLayers.enable( 17 ); + if ( parameters.displacementMap ) + _programLayers.enable( 18 ); + if ( parameters.specularMap ) + _programLayers.enable( 19 ); + if ( parameters.roughnessMap ) + _programLayers.enable( 20 ); + if ( parameters.metalnessMap ) + _programLayers.enable( 21 ); + if ( parameters.gradientMap ) + _programLayers.enable( 22 ); + if ( parameters.alphaMap ) + _programLayers.enable( 23 ); + if ( parameters.alphaTest ) + _programLayers.enable( 24 ); + if ( parameters.vertexColors ) + _programLayers.enable( 25 ); + if ( parameters.vertexAlphas ) + _programLayers.enable( 26 ); + if ( parameters.vertexUvs ) + _programLayers.enable( 27 ); + if ( parameters.vertexTangents ) + _programLayers.enable( 28 ); + if ( parameters.uvsVertexOnly ) + _programLayers.enable( 29 ); + if ( parameters.fog ) + _programLayers.enable( 30 ); + + array.push( _programLayers.mask ); + _programLayers.disableAll(); + + if ( parameters.useFog ) + _programLayers.enable( 0 ); + if ( parameters.flatShading ) + _programLayers.enable( 1 ); + if ( parameters.logarithmicDepthBuffer ) + _programLayers.enable( 2 ); + if ( parameters.skinning ) + _programLayers.enable( 3 ); + if ( parameters.useVertexTexture ) + _programLayers.enable( 4 ); + if ( parameters.morphTargets ) + _programLayers.enable( 5 ); + if ( parameters.morphNormals ) + _programLayers.enable( 6 ); + if ( parameters.morphColors ) + _programLayers.enable( 7 ); + if ( parameters.premultipliedAlpha ) + _programLayers.enable( 8 ); + if ( parameters.shadowMapEnabled ) + _programLayers.enable( 9 ); + if ( parameters.physicallyCorrectLights ) + _programLayers.enable( 10 ); + if ( parameters.doubleSided ) + _programLayers.enable( 11 ); + if ( parameters.flipSided ) + _programLayers.enable( 12 ); + if ( parameters.depthPacking ) + _programLayers.enable( 13 ); + if ( parameters.dithering ) + _programLayers.enable( 14 ); + if ( parameters.specularIntensityMap ) + _programLayers.enable( 15 ); + if ( parameters.specularColorMap ) + _programLayers.enable( 16 ); + if ( parameters.transmission ) + _programLayers.enable( 17 ); + if ( parameters.transmissionMap ) + _programLayers.enable( 18 ); + if ( parameters.thicknessMap ) + _programLayers.enable( 19 ); + if ( parameters.sheen ) + _programLayers.enable( 20 ); + if ( parameters.sheenColorMap ) + _programLayers.enable( 21 ); + if ( parameters.sheenRoughnessMap ) + _programLayers.enable( 22 ); + if ( parameters.decodeVideoTexture ) + _programLayers.enable( 23 ); + if ( parameters.opaque ) + _programLayers.enable( 24 ); + + array.push( _programLayers.mask ); - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; + } - } + function getUniforms( material ) { - let cachedMaterial = materialsForVariant[ keyB ]; + const shaderID = shaderIDs[ material.type ]; + let uniforms; - if ( cachedMaterial === undefined ) { + if ( shaderID ) { - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; + const shader = ShaderLib[ shaderID ]; + uniforms = UniformsUtils.clone( shader.uniforms ); - } + } else { - result = cachedMaterial; + uniforms = material.uniforms; } - result.visible = material.visible; - result.wireframe = material.wireframe; + return uniforms; - if ( type === VSMShadowMap ) { + } - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; + function acquireProgram( parameters, cacheKey ) { - } else { + let program; - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; + // Check if code has been already compiled + for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - } + const preexistingProgram = programs[ p ]; - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; + if ( preexistingProgram.cacheKey === cacheKey ) { - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; + program = preexistingProgram; + ++ program.usedTimes; - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { + break; - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; + } } - return result; + if ( program === undefined ) { - } + program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); + programs.push( program ); - function renderObject( object, camera, shadowCamera, light, type ) { + } - if ( object.visible === false ) return; + return program; - const visible = object.layers.test( camera.layers ); + } - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { + function releaseProgram( program ) { - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { + if ( -- program.usedTimes === 0 ) { - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + // Remove from unordered set + const i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); - const geometry = _objects.update( object ); - const material = object.material; + // Free WebGL resources + program.destroy(); - if ( Array.isArray( material ) ) { + } - const groups = geometry.groups; + } - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { + function releaseShaderCache( material ) { - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; + _customShaders.remove( material ); - if ( groupMaterial && groupMaterial.visible ) { + } - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); + function dispose() { - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); + _customShaders.dispose(); - } + } - } + return { + getParameters: getParameters, + getProgramCacheKey: getProgramCacheKey, + getUniforms: getUniforms, + acquireProgram: acquireProgram, + releaseProgram: releaseProgram, + releaseShaderCache: releaseShaderCache, + // Exposed for resource monitoring & error feedback via renderer.info: + programs: programs, + dispose: dispose + }; - } else if ( material.visible ) { + } - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); + function WebGLProperties() { - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); + let properties = new WeakMap(); - } + function get( object ) { - } + let map = properties.get( object ); + + if ( map === undefined ) { + + map = {}; + properties.set( object, map ); } - const children = object.children; + return map; - for ( let i = 0, l = children.length; i < l; i ++ ) { + } - renderObject( children[ i ], camera, shadowCamera, light, type ); + function remove( object ) { - } + properties.delete( object ); } - } + function update( object, key, value ) { - function WebGLState( gl, extensions, capabilities ) { + properties.get( object )[ key ] = value; - const isWebGL2 = capabilities.isWebGL2; + } - function ColorBuffer() { + function dispose() { - let locked = false; + properties = new WeakMap(); - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); + } - return { + return { + get: get, + remove: remove, + update: update, + dispose: dispose + }; - setMask: function ( colorMask ) { + } - if ( currentColorMask !== colorMask && ! locked ) { + function painterSortStable( a, b ) { - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; + if ( a.groupOrder !== b.groupOrder ) { - } + return a.groupOrder - b.groupOrder; - }, + } else if ( a.renderOrder !== b.renderOrder ) { - setLocked: function ( lock ) { + return a.renderOrder - b.renderOrder; - locked = lock; + } else if ( a.material.id !== b.material.id ) { - }, + return a.material.id - b.material.id; - setClear: function ( r, g, b, a, premultipliedAlpha ) { + } else if ( a.z !== b.z ) { - if ( premultipliedAlpha === true ) { + return a.z - b.z; - r *= a; g *= a; b *= a; + } else { - } + return a.id - b.id; - color.set( r, g, b, a ); + } - if ( currentColorClear.equals( color ) === false ) { + } - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); + function reversePainterSortStable( a, b ) { - } + if ( a.groupOrder !== b.groupOrder ) { - }, + return a.groupOrder - b.groupOrder; - reset: function () { + } else if ( a.renderOrder !== b.renderOrder ) { - locked = false; + return a.renderOrder - b.renderOrder; - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state + } else if ( a.z !== b.z ) { - } + return b.z - a.z; - }; + } else { + + return a.id - b.id; } - function DepthBuffer() { + } - let locked = false; - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; + function WebGLRenderList() { - return { + const renderItems = []; + let renderItemsIndex = 0; - setTest: function ( depthTest ) { + const opaque = []; + const transmissive = []; + const transparent = []; - if ( depthTest ) { + function init() { - enable( 2929 ); + renderItemsIndex = 0; - } else { + opaque.length = 0; + transmissive.length = 0; + transparent.length = 0; - disable( 2929 ); + } - } + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - }, + let renderItem = renderItems[ renderItemsIndex ]; - setMask: function ( depthMask ) { + if ( renderItem === undefined ) { - if ( currentDepthMask !== depthMask && ! locked ) { + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + groupOrder: groupOrder, + renderOrder: object.renderOrder, + z: z, + group: group + }; - gl.depthMask( depthMask ); - currentDepthMask = depthMask; + renderItems[ renderItemsIndex ] = renderItem; - } + } else { - }, + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.groupOrder = groupOrder; + renderItem.renderOrder = object.renderOrder; + renderItem.z = z; + renderItem.group = group; - setFunc: function ( depthFunc ) { + } - if ( currentDepthFunc !== depthFunc ) { + renderItemsIndex ++; - if ( depthFunc ) { + return renderItem; - switch ( depthFunc ) { + } - case NeverDepth: + function push( object, geometry, material, groupOrder, z, group ) { - gl.depthFunc( 512 ); - break; + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - case AlwaysDepth: + if ( material.transmission > 0.0 ) { - gl.depthFunc( 519 ); - break; + transmissive.push( renderItem ); - case LessDepth: + } else if ( material.transparent === true ) { - gl.depthFunc( 513 ); - break; + transparent.push( renderItem ); - case LessEqualDepth: + } else { - gl.depthFunc( 515 ); - break; + opaque.push( renderItem ); - case EqualDepth: + } - gl.depthFunc( 514 ); - break; + } - case GreaterEqualDepth: + function unshift( object, geometry, material, groupOrder, z, group ) { - gl.depthFunc( 518 ); - break; + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - case GreaterDepth: + if ( material.transmission > 0.0 ) { - gl.depthFunc( 516 ); - break; + transmissive.unshift( renderItem ); - case NotEqualDepth: + } else if ( material.transparent === true ) { - gl.depthFunc( 517 ); - break; + transparent.unshift( renderItem ); - default: + } else { - gl.depthFunc( 515 ); + opaque.unshift( renderItem ); - } + } - } else { + } - gl.depthFunc( 515 ); + function sort( customOpaqueSort, customTransparentSort ) { - } + if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); + if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); + if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - currentDepthFunc = depthFunc; + } - } + function finish() { - }, + // Clear references from inactive renderItems in the list - setLocked: function ( lock ) { + for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - locked = lock; + const renderItem = renderItems[ i ]; - }, + if ( renderItem.id === null ) break; - setClear: function ( depth ) { + renderItem.id = null; + renderItem.object = null; + renderItem.geometry = null; + renderItem.material = null; + renderItem.group = null; - if ( currentDepthClear !== depth ) { + } - gl.clearDepth( depth ); - currentDepthClear = depth; + } - } + return { - }, + opaque: opaque, + transmissive: transmissive, + transparent: transparent, - reset: function () { + init: init, + push: push, + unshift: unshift, + finish: finish, - locked = false; + sort: sort + }; - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; + } - } + function WebGLRenderLists() { - }; + let lists = new WeakMap(); - } + function get( scene, renderCallDepth ) { - function StencilBuffer() { + let list; - let locked = false; + if ( lists.has( scene ) === false ) { - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; + list = new WebGLRenderList(); + lists.set( scene, [ list ] ); - return { + } else { - setTest: function ( stencilTest ) { + if ( renderCallDepth >= lists.get( scene ).length ) { - if ( ! locked ) { + list = new WebGLRenderList(); + lists.get( scene ).push( list ); - if ( stencilTest ) { + } else { - enable( 2960 ); + list = lists.get( scene )[ renderCallDepth ]; - } else { + } - disable( 2960 ); + } - } + return list; - } + } - }, + function dispose() { - setMask: function ( stencilMask ) { + lists = new WeakMap(); - if ( currentStencilMask !== stencilMask && ! locked ) { + } - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; + return { + get: get, + dispose: dispose + }; - } + } - }, + function UniformsCache() { - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { + const lights = {}; - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { + return { - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); + get: function ( light ) { - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; + if ( lights[ light.id ] !== undefined ) { - } + return lights[ light.id ]; - }, + } - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { + let uniforms; - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { + switch ( light.type ) { - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); + case 'DirectionalLight': + uniforms = { + direction: new Vector3(), + color: new Color() + }; + break; - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; + case 'SpotLight': + uniforms = { + position: new Vector3(), + direction: new Vector3(), + color: new Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0 + }; + break; - } + case 'PointLight': + uniforms = { + position: new Vector3(), + color: new Color(), + distance: 0, + decay: 0 + }; + break; - }, + case 'HemisphereLight': + uniforms = { + direction: new Vector3(), + skyColor: new Color(), + groundColor: new Color() + }; + break; - setLocked: function ( lock ) { + case 'RectAreaLight': + uniforms = { + color: new Color(), + position: new Vector3(), + halfWidth: new Vector3(), + halfHeight: new Vector3() + }; + break; - locked = lock; + } - }, + lights[ light.id ] = uniforms; - setClear: function ( stencil ) { + return uniforms; - if ( currentStencilClear !== stencil ) { - - gl.clearStencil( stencil ); - currentStencilClear = stencil; - - } - - }, + } - reset: function () { + }; - locked = false; + } - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; + function ShadowUniformsCache() { - } + const lights = {}; - }; + return { - } + get: function ( light ) { - // + if ( lights[ light.id ] !== undefined ) { - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); + return lights[ light.id ]; - let enabledCapabilities = {}; + } - let xrFramebuffer = null; - let currentBoundFramebuffers = {}; + let uniforms; - let currentProgram = null; + switch ( light.type ) { - let currentBlendingEnabled = false; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; + case 'DirectionalLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - let currentFlipSided = null; - let currentCullFace = null; + case 'SpotLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - let currentLineWidth = null; + case 'PointLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2(), + shadowCameraNear: 1, + shadowCameraFar: 1000 + }; + break; - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; + // TODO (abelnation): set RectAreaLight shadow uniforms - const maxTextures = gl.getParameter( 35661 ); + } - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); + lights[ light.id ] = uniforms; - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { + return uniforms; - version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); + } - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { + }; - version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); + } - } - let currentTextureSlot = null; - let currentBoundTextures = {}; - const currentScissor = new Vector4( 0, 0, gl.canvas.width, gl.canvas.height ); - const currentViewport = new Vector4( 0, 0, gl.canvas.width, gl.canvas.height ); + let nextVersion = 0; - function createTexture( type, target, count ) { + function shadowCastingLightsFirst( lightA, lightB ) { - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); + return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); + } - for ( let i = 0; i < count; i ++ ) { + function WebGLLights( extensions, capabilities ) { - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); + const cache = new UniformsCache(); - } + const shadowCache = ShadowUniformsCache(); - return texture; + const state = { - } + version: 0, - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); + hash: { + directionalLength: - 1, + pointLength: - 1, + spotLength: - 1, + rectAreaLength: - 1, + hemiLength: - 1, - // init + numDirectionalShadows: - 1, + numPointShadows: - 1, + numSpotShadows: - 1 + }, - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); + ambient: [ 0, 0, 0 ], + probe: [], + directional: [], + directionalShadow: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadow: [], + spotShadowMap: [], + spotShadowMatrix: [], + rectArea: [], + rectAreaLTC1: null, + rectAreaLTC2: null, + point: [], + pointShadow: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [] - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); + }; - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); + for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - setBlending( NoBlending ); + const vector3 = new Vector3(); + const matrix4 = new Matrix4(); + const matrix42 = new Matrix4(); - // + function setup( lights, physicallyCorrectLights ) { - function enable( id ) { + let r = 0, g = 0, b = 0; - if ( enabledCapabilities[ id ] !== true ) { + for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - gl.enable( id ); - enabledCapabilities[ id ] = true; + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - } + let numDirectionalShadows = 0; + let numPointShadows = 0; + let numSpotShadows = 0; - } + lights.sort( shadowCastingLightsFirst ); - function disable( id ) { + // artist-friendly light intensity scaling factor + const scaleFactor = ( physicallyCorrectLights !== true ) ? Math.PI : 1; - if ( enabledCapabilities[ id ] !== false ) { + for ( let i = 0, l = lights.length; i < l; i ++ ) { - gl.disable( id ); - enabledCapabilities[ id ] = false; + const light = lights[ i ]; - } + const color = light.color; + const intensity = light.intensity; + const distance = light.distance; - } + const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - function bindXRFramebuffer( framebuffer ) { + if ( light.isAmbientLight ) { - if ( framebuffer !== xrFramebuffer ) { + r += color.r * intensity * scaleFactor; + g += color.g * intensity * scaleFactor; + b += color.b * intensity * scaleFactor; - gl.bindFramebuffer( 36160, framebuffer ); + } else if ( light.isLightProbe ) { - xrFramebuffer = framebuffer; + for ( let j = 0; j < 9; j ++ ) { - } + state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - } + } - function bindFramebuffer( target, framebuffer ) { + } else if ( light.isDirectionalLight ) { - if ( framebuffer === null && xrFramebuffer !== null ) framebuffer = xrFramebuffer; // use active XR framebuffer if available + const uniforms = cache.get( light ); - if ( currentBoundFramebuffers[ target ] !== framebuffer ) { + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - gl.bindFramebuffer( target, framebuffer ); + if ( light.castShadow ) { - currentBoundFramebuffers[ target ] = framebuffer; + const shadow = light.shadow; - if ( isWebGL2 ) { + const shadowUniforms = shadowCache.get( light ); - // 36009 is equivalent to 36160 + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - if ( target === 36009 ) { + state.directionalShadow[ directionalLength ] = shadowUniforms; + state.directionalShadowMap[ directionalLength ] = shadowMap; + state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - currentBoundFramebuffers[ 36160 ] = framebuffer; + numDirectionalShadows ++; } - if ( target === 36160 ) { - - currentBoundFramebuffers[ 36009 ] = framebuffer; - - } + state.directional[ directionalLength ] = uniforms; - } + directionalLength ++; - } + } else if ( light.isSpotLight ) { - } + const uniforms = cache.get( light ); - function useProgram( program ) { + uniforms.position.setFromMatrixPosition( light.matrixWorld ); - if ( currentProgram !== program ) { + uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); + uniforms.distance = distance; - gl.useProgram( program ); + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = light.decay; - currentProgram = program; + if ( light.castShadow ) { - return true; + const shadow = light.shadow; - } + const shadowUniforms = shadowCache.get( light ); - return false; + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - } + state.spotShadow[ spotLength ] = shadowUniforms; + state.spotShadowMap[ spotLength ] = shadowMap; + state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; + numSpotShadows ++; - if ( isWebGL2 ) { + } - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; + state.spot[ spotLength ] = uniforms; - } else { + spotLength ++; - const extension = extensions.get( 'EXT_blend_minmax' ); + } else if ( light.isRectAreaLight ) { - if ( extension !== null ) { + const uniforms = cache.get( light ); - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; + // (a) intensity is the total visible light emitted + //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - } + // (b) intensity is the brightness of the light + uniforms.color.copy( color ).multiplyScalar( intensity ); - } + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; + state.rectArea[ rectAreaLength ] = uniforms; - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + rectAreaLength ++; - if ( blending === NoBlending ) { + } else if ( light.isPointLight ) { - if ( currentBlendingEnabled === true ) { + const uniforms = cache.get( light ); - disable( 3042 ); - currentBlendingEnabled = false; + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); + uniforms.distance = light.distance; + uniforms.decay = light.decay; - } + if ( light.castShadow ) { - return; + const shadow = light.shadow; - } + const shadowUniforms = shadowCache.get( light ); - if ( currentBlendingEnabled === false ) { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; + shadowUniforms.shadowCameraNear = shadow.camera.near; + shadowUniforms.shadowCameraFar = shadow.camera.far; - enable( 3042 ); - currentBlendingEnabled = true; + state.pointShadow[ pointLength ] = shadowUniforms; + state.pointShadowMap[ pointLength ] = shadowMap; + state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - } + numPointShadows ++; - if ( blending !== CustomBlending ) { + } - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + state.point[ pointLength ] = uniforms; - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { + pointLength ++; - gl.blendEquation( 32774 ); + } else if ( light.isHemisphereLight ) { - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; + const uniforms = cache.get( light ); - } + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); - if ( premultipliedAlpha ) { + state.hemi[ hemiLength ] = uniforms; - switch ( blending ) { + hemiLength ++; - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; + } - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; + } - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; + if ( rectAreaLength > 0 ) { - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; + if ( capabilities.isWebGL2 ) { - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + // WebGL 2 - } + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - } else { + } else { - switch ( blending ) { + // WebGL 1 - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; + if ( extensions.has( 'OES_texture_float_linear' ) === true ) { - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; + } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; + state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; + state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + } else { - } + console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); } - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; - } - return; - } - // custom blending - - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + state.ambient[ 0 ] = r; + state.ambient[ 1 ] = g; + state.ambient[ 2 ] = b; - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + const hash = state.hash; - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); + if ( hash.directionalLength !== directionalLength || + hash.pointLength !== pointLength || + hash.spotLength !== spotLength || + hash.rectAreaLength !== rectAreaLength || + hash.hemiLength !== hemiLength || + hash.numDirectionalShadows !== numDirectionalShadows || + hash.numPointShadows !== numPointShadows || + hash.numSpotShadows !== numSpotShadows ) { - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; - } + state.directionalShadow.length = numDirectionalShadows; + state.directionalShadowMap.length = numDirectionalShadows; + state.pointShadow.length = numPointShadows; + state.pointShadowMap.length = numPointShadows; + state.spotShadow.length = numSpotShadows; + state.spotShadowMap.length = numSpotShadows; + state.directionalShadowMatrix.length = numDirectionalShadows; + state.pointShadowMatrix.length = numPointShadows; + state.spotShadowMatrix.length = numSpotShadows; - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + hash.directionalLength = directionalLength; + hash.pointLength = pointLength; + hash.spotLength = spotLength; + hash.rectAreaLength = rectAreaLength; + hash.hemiLength = hemiLength; - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); + hash.numDirectionalShadows = numDirectionalShadows; + hash.numPointShadows = numPointShadows; + hash.numSpotShadows = numSpotShadows; - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + state.version = nextVersion ++; } - currentBlending = blending; - currentPremultipledAlpha = null; - } - function setMaterial( material, frontFaceCW ) { + function setupView( lights, camera ) { - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; + const viewMatrix = camera.matrixWorldInverse; - setFlipSided( flipSided ); + for ( let i = 0, l = lights.length; i < l; i ++ ) { - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + const light = lights[ i ]; - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); + if ( light.isDirectionalLight ) { - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { + const uniforms = state.directional[ directionalLength ]; - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - } + directionalLength ++; - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + } else if ( light.isSpotLight ) { - material.alphaToCoverage === true - ? enable( 32926 ) - : disable( 32926 ); + const uniforms = state.spot[ spotLength ]; - } + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - // + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - function setFlipSided( flipSided ) { + spotLength ++; - if ( currentFlipSided !== flipSided ) { - - if ( flipSided ) { - - gl.frontFace( 2304 ); - - } else { - - gl.frontFace( 2305 ); - - } + } else if ( light.isRectAreaLight ) { - currentFlipSided = flipSided; + const uniforms = state.rectArea[ rectAreaLength ]; - } + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } + // extract local rotation of light to derive width/height half vectors + matrix42.identity(); + matrix4.copy( light.matrixWorld ); + matrix4.premultiply( viewMatrix ); + matrix42.extractRotation( matrix4 ); - function setCullFace( cullFace ) { + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - if ( cullFace !== CullFaceNone ) { + uniforms.halfWidth.applyMatrix4( matrix42 ); + uniforms.halfHeight.applyMatrix4( matrix42 ); - enable( 2884 ); + rectAreaLength ++; - if ( cullFace !== currentCullFace ) { + } else if ( light.isPointLight ) { - if ( cullFace === CullFaceBack ) { + const uniforms = state.point[ pointLength ]; - gl.cullFace( 1029 ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } else if ( cullFace === CullFaceFront ) { + pointLength ++; - gl.cullFace( 1028 ); + } else if ( light.isHemisphereLight ) { - } else { + const uniforms = state.hemi[ hemiLength ]; - gl.cullFace( 1032 ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); - } + hemiLength ++; } - } else { - - disable( 2884 ); - } - currentCullFace = cullFace; - } - function setLineWidth( width ) { + return { + setup: setup, + setupView: setupView, + state: state + }; - if ( width !== currentLineWidth ) { + } - if ( lineWidthAvailable ) gl.lineWidth( width ); + function WebGLRenderState( extensions, capabilities ) { - currentLineWidth = width; + const lights = new WebGLLights( extensions, capabilities ); - } + const lightsArray = []; + const shadowsArray = []; - } + function init() { - function setPolygonOffset( polygonOffset, factor, units ) { + lightsArray.length = 0; + shadowsArray.length = 0; - if ( polygonOffset ) { + } - enable( 32823 ); + function pushLight( light ) { - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { + lightsArray.push( light ); - gl.polygonOffset( factor, units ); + } - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; + function pushShadow( shadowLight ) { - } + shadowsArray.push( shadowLight ); - } else { + } - disable( 32823 ); + function setupLights( physicallyCorrectLights ) { - } + lights.setup( lightsArray, physicallyCorrectLights ); } - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { + function setupLightsView( camera ) { - enable( 3089 ); + lights.setupView( lightsArray, camera ); - } else { + } - disable( 3089 ); + const state = { + lightsArray: lightsArray, + shadowsArray: shadowsArray, - } + lights: lights + }; - } + return { + init: init, + state: state, + setupLights: setupLights, + setupLightsView: setupLightsView, - // texture + pushLight: pushLight, + pushShadow: pushShadow + }; - function activeTexture( webglSlot ) { + } - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; + function WebGLRenderStates( extensions, capabilities ) { - if ( currentTextureSlot !== webglSlot ) { + let renderStates = new WeakMap(); - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; + function get( scene, renderCallDepth = 0 ) { - } + let renderState; - } + if ( renderStates.has( scene ) === false ) { - function bindTexture( webglType, webglTexture ) { + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.set( scene, [ renderState ] ); - if ( currentTextureSlot === null ) { + } else { - activeTexture(); + if ( renderCallDepth >= renderStates.get( scene ).length ) { - } + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.get( scene ).push( renderState ); - let boundTexture = currentBoundTextures[ currentTextureSlot ]; + } else { - if ( boundTexture === undefined ) { + renderState = renderStates.get( scene )[ renderCallDepth ]; - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; + } } - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + return renderState; - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); + } - boundTexture.type = webglType; - boundTexture.texture = webglTexture; + function dispose() { - } + renderStates = new WeakMap(); } - function unbindTexture() { + return { + get: get, + dispose: dispose + }; - const boundTexture = currentBoundTextures[ currentTextureSlot ]; + } - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { + class MeshDepthMaterial extends Material { - gl.bindTexture( boundTexture.type, null ); + constructor( parameters ) { - boundTexture.type = undefined; - boundTexture.texture = undefined; + super(); - } + this.type = 'MeshDepthMaterial'; - } + this.depthPacking = BasicDepthPacking; - function compressedTexImage2D() { + this.map = null; - try { + this.alphaMap = null; - gl.compressedTexImage2D.apply( gl, arguments ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } catch ( error ) { + this.wireframe = false; + this.wireframeLinewidth = 1; - console.error( 'THREE.WebGLState:', error ); + this.fog = false; - } + this.setValues( parameters ); } - function texImage2D() { + copy( source ) { - try { + super.copy( source ); - gl.texImage2D.apply( gl, arguments ); + this.depthPacking = source.depthPacking; - } catch ( error ) { + this.map = source.map; - console.error( 'THREE.WebGLState:', error ); + this.alphaMap = source.alphaMap; - } + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + return this; } - function texImage3D() { + } - try { + MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - gl.texImage3D.apply( gl, arguments ); + class MeshDistanceMaterial extends Material { - } catch ( error ) { + constructor( parameters ) { - console.error( 'THREE.WebGLState:', error ); + super(); - } + this.type = 'MeshDistanceMaterial'; - } + this.referencePosition = new Vector3(); + this.nearDistance = 1; + this.farDistance = 1000; - // + this.map = null; - function scissor( scissor ) { + this.alphaMap = null; - if ( currentScissor.equals( scissor ) === false ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); + this.fog = false; - } + this.setValues( parameters ); } - function viewport( viewport ) { + copy( source ) { - if ( currentViewport.equals( viewport ) === false ) { + super.copy( source ); - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); + this.referencePosition.copy( source.referencePosition ); + this.nearDistance = source.nearDistance; + this.farDistance = source.farDistance; - } + this.map = source.map; - } + this.alphaMap = source.alphaMap; - // + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - function reset() { + return this; - // reset state + } - gl.disable( 3042 ); - gl.disable( 2884 ); - gl.disable( 2929 ); - gl.disable( 32823 ); - gl.disable( 3089 ); - gl.disable( 2960 ); - gl.disable( 32926 ); + } - gl.blendEquation( 32774 ); - gl.blendFunc( 1, 0 ); - gl.blendFuncSeparate( 1, 0, 1, 0 ); + MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - gl.colorMask( true, true, true, true ); - gl.clearColor( 0, 0, 0, 0 ); + const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - gl.depthMask( true ); - gl.depthFunc( 513 ); - gl.clearDepth( 1 ); + const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - gl.stencilMask( 0xffffffff ); - gl.stencilFunc( 519, 0, 0xffffffff ); - gl.stencilOp( 7680, 7680, 7680 ); - gl.clearStencil( 0 ); + function WebGLShadowMap( _renderer, _objects, _capabilities ) { - gl.cullFace( 1029 ); - gl.frontFace( 2305 ); + let _frustum = new Frustum(); - gl.polygonOffset( 0, 0 ); + const _shadowMapSize = new Vector2(), + _viewportSize = new Vector2(), - gl.activeTexture( 33984 ); + _viewport = new Vector4(), - gl.bindFramebuffer( 36160, null ); + _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), + _distanceMaterial = new MeshDistanceMaterial(), - if ( isWebGL2 === true ) { + _materialCache = {}, - gl.bindFramebuffer( 36009, null ); - gl.bindFramebuffer( 36008, null ); + _maxTextureSize = _capabilities.maxTextureSize; - } + const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - gl.useProgram( null ); + const shadowMaterialVertical = new ShaderMaterial( { + defines: { + VSM_SAMPLES: 8 + }, + uniforms: { + shadow_pass: { value: null }, + resolution: { value: new Vector2() }, + radius: { value: 4.0 } + }, - gl.lineWidth( 1 ); + vertexShader: vertex, + fragmentShader: fragment - gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); - gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + } ); - // reset internals + const shadowMaterialHorizontal = shadowMaterialVertical.clone(); + shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; - enabledCapabilities = {}; + const fullScreenTri = new BufferGeometry(); + fullScreenTri.setAttribute( + 'position', + new BufferAttribute( + new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), + 3 + ) + ); - currentTextureSlot = null; - currentBoundTextures = {}; + const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - xrFramebuffer = null; - currentBoundFramebuffers = {}; + const scope = this; - currentProgram = null; + this.enabled = false; - currentBlendingEnabled = false; - currentBlending = null; - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - currentPremultipledAlpha = false; + this.autoUpdate = true; + this.needsUpdate = false; - currentFlipSided = null; - currentCullFace = null; + this.type = PCFShadowMap; - currentLineWidth = null; + this.render = function ( lights, scene, camera ) { - currentPolygonOffsetFactor = null; - currentPolygonOffsetUnits = null; + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); - currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); + if ( lights.length === 0 ) return; - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); + const currentRenderTarget = _renderer.getRenderTarget(); + const activeCubeFace = _renderer.getActiveCubeFace(); + const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - } + const _state = _renderer.state; - return { + // Set GL state for depth map. + _state.setBlending( NoBlending ); + _state.buffers.color.setClear( 1, 1, 1, 1 ); + _state.buffers.depth.setTest( true ); + _state.setScissorTest( false ); - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, + // render depth map - enable: enable, - disable: disable, + for ( let i = 0, il = lights.length; i < il; i ++ ) { - bindFramebuffer: bindFramebuffer, - bindXRFramebuffer: bindXRFramebuffer, + const light = lights[ i ]; + const shadow = light.shadow; - useProgram: useProgram, + if ( shadow === undefined ) { - setBlending: setBlending, - setMaterial: setMaterial, + console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + continue; - setFlipSided: setFlipSided, - setCullFace: setCullFace, + } - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, + if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - setScissorTest: setScissorTest, + _shadowMapSize.copy( shadow.mapSize ); - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, + const shadowFrameExtents = shadow.getFrameExtents(); - scissor: scissor, - viewport: viewport, + _shadowMapSize.multiply( shadowFrameExtents ); - reset: reset + _viewportSize.copy( shadow.mapSize ); - }; + if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { - } + if ( _shadowMapSize.x > _maxTextureSize ) { - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { + _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); + _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; + shadow.mapSize.x = _viewportSize.x; - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; + } - const _videoTextures = new WeakMap(); - let _canvas; + if ( _shadowMapSize.y > _maxTextureSize ) { - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). + _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); + _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; + shadow.mapSize.y = _viewportSize.y; - let useOffscreenCanvas = false; + } - try { + } - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; + if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - } catch ( err ) { + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); + shadow.map.texture.name = light.name + '.shadowMap'; - // Ignore any errors + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); - } + shadow.camera.updateProjectionMatrix(); - function createCanvas( width, height ) { + } - // Use OffscreenCanvas when available. Specially needed in web workers + if ( shadow.map === null ) { - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - } + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + '.shadowMap'; - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { + shadow.camera.updateProjectionMatrix(); - let scale = 1; + } - // handle case if texture exceeds max size + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); - if ( image.width > maxSize || image.height > maxSize ) { + const viewportCount = shadow.getViewportCount(); - scale = maxSize / Math.max( image.width, image.height ); + for ( let vp = 0; vp < viewportCount; vp ++ ) { - } + const viewport = shadow.getViewport( vp ); - // only perform resize if necessary + _viewport.set( + _viewportSize.x * viewport.x, + _viewportSize.y * viewport.y, + _viewportSize.x * viewport.z, + _viewportSize.y * viewport.w + ); - if ( scale < 1 || needsPowerOfTwo === true ) { + _state.viewport( _viewport ); - // only perform resize for certain image types + shadow.updateMatrices( light, vp ); - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { + _frustum = shadow.getFrustum(); - const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; + renderObject( scene, camera, shadow.camera, light, this.type ); - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); + } - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); + // do blur pass for VSM - // cube textures can't reuse the same canvas + if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; + VSMPass( shadow, camera ); - canvas.width = width; - canvas.height = height; + } - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); + shadow.needsUpdate = false; - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); + } - return canvas; + scope.needsUpdate = false; - } else { + _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - if ( 'data' in image ) { + }; - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); + function VSMPass( shadow, camera ) { - } + const geometry = _objects.update( fullScreenMesh ); - return image; + if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { - } + shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; + shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; - } + shadowMaterialVertical.needsUpdate = true; + shadowMaterialHorizontal.needsUpdate = true; - return image; + } - } + // vertical pass - function isPowerOfTwo$1( image ) { - - return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - - } - - function textureNeedsPowerOfTwo( texture ) { + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.mapPass ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - if ( isWebGL2 ) return false; + // horizontal pass - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); + shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); } - function textureNeedsGenerateMipmaps( texture, supportsMips ) { + function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar, type ) { - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + let result = null; - } + const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; - function generateMipmap( target, texture, width, height ) { + if ( customMaterial !== undefined ) { - _gl.generateMipmap( target ); + result = customMaterial; - const textureProperties = properties.get( texture ); + } else { - textureProperties.__maxMipLevel = Math.log2( Math.max( width, height ) ); + result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; - } + } - function getInternalFormat( internalFormatName, glFormat, glType ) { + if ( ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) || + ( material.displacementMap && material.displacementScale !== 0 ) || + ( material.alphaMap && material.alphaTest > 0 ) ) { - if ( isWebGL2 === false ) return glFormat; + // in this case we need a unique material instance reflecting the + // appropriate state - if ( internalFormatName !== null ) { + const keyA = result.uuid, keyB = material.uuid; - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; + let materialsForVariant = _materialCache[ keyA ]; - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); + if ( materialsForVariant === undefined ) { - } + materialsForVariant = {}; + _materialCache[ keyA ] = materialsForVariant; - let internalFormat = glFormat; + } - if ( glFormat === 6403 ) { + let cachedMaterial = materialsForVariant[ keyB ]; - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; + if ( cachedMaterial === undefined ) { - } + cachedMaterial = result.clone(); + materialsForVariant[ keyB ] = cachedMaterial; - if ( glFormat === 6407 ) { + } - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; + result = cachedMaterial; } - if ( glFormat === 6408 ) { + result.visible = material.visible; + result.wireframe = material.wireframe; - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; + if ( type === VSMShadowMap ) { - } + result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { + } else { - extensions.get( 'EXT_color_buffer_float' ); + result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } - return internalFormat; + result.alphaMap = material.alphaMap; + result.alphaTest = material.alphaTest; - } + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; + result.clipIntersection = material.clipIntersection; - // Fallback filters for non-power-of-2 textures + result.displacementMap = material.displacementMap; + result.displacementScale = material.displacementScale; + result.displacementBias = material.displacementBias; - function filterFallback( f ) { + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { + if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - return 9728; + result.referencePosition.setFromMatrixPosition( light.matrixWorld ); + result.nearDistance = shadowCameraNear; + result.farDistance = shadowCameraFar; } - return 9729; + return result; } - // + function renderObject( object, camera, shadowCamera, light, type ) { - function onTextureDispose( event ) { + if ( object.visible === false ) return; - const texture = event.target; + const visible = object.layers.test( camera.layers ); - texture.removeEventListener( 'dispose', onTextureDispose ); + if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - deallocateTexture( texture ); + if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - if ( texture.isVideoTexture ) { + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - _videoTextures.delete( texture ); + const geometry = _objects.update( object ); + const material = object.material; - } + if ( Array.isArray( material ) ) { - info.memory.textures --; + const groups = geometry.groups; - } + for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - function onRenderTargetDispose( event ) { + const group = groups[ k ]; + const groupMaterial = material[ group.materialIndex ]; - const renderTarget = event.target; + if ( groupMaterial && groupMaterial.visible ) { - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + const depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - deallocateRenderTarget( renderTarget ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - info.memory.textures --; + } - } + } - // + } else if ( material.visible ) { - function deallocateTexture( texture ) { + const depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far, type ); - const textureProperties = properties.get( texture ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - if ( textureProperties.__webglInit === undefined ) return; + } - _gl.deleteTexture( textureProperties.__webglTexture ); + } - properties.remove( texture ); + } - } + const children = object.children; - function deallocateRenderTarget( renderTarget ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - const texture = renderTarget.texture; + renderObject( children[ i ], camera, shadowCamera, light, type ); - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); + } - if ( ! renderTarget ) return; + } - if ( textureProperties.__webglTexture !== undefined ) { + } - _gl.deleteTexture( textureProperties.__webglTexture ); + function WebGLState( gl, extensions, capabilities ) { - } + const isWebGL2 = capabilities.isWebGL2; - if ( renderTarget.depthTexture ) { + function ColorBuffer() { - renderTarget.depthTexture.dispose(); + let locked = false; - } + const color = new Vector4(); + let currentColorMask = null; + const currentColorClear = new Vector4( 0, 0, 0, 0 ); - if ( renderTarget.isWebGLCubeRenderTarget ) { + return { - for ( let i = 0; i < 6; i ++ ) { + setMask: function ( colorMask ) { - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); + if ( currentColorMask !== colorMask && ! locked ) { - } + gl.colorMask( colorMask, colorMask, colorMask, colorMask ); + currentColorMask = colorMask; - } else { + } - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); + }, - } + setLocked: function ( lock ) { - properties.remove( texture ); - properties.remove( renderTarget ); + locked = lock; - } + }, - // + setClear: function ( r, g, b, a, premultipliedAlpha ) { - let textureUnits = 0; + if ( premultipliedAlpha === true ) { - function resetTextureUnits() { + r *= a; g *= a; b *= a; - textureUnits = 0; + } - } + color.set( r, g, b, a ); - function allocateTextureUnit() { + if ( currentColorClear.equals( color ) === false ) { - const textureUnit = textureUnits; + gl.clearColor( r, g, b, a ); + currentColorClear.copy( color ); - if ( textureUnit >= maxTextures ) { + } - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); + }, - } + reset: function () { - textureUnits += 1; + locked = false; - return textureUnit; + currentColorMask = null; + currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - } + } - // + }; - function setTexture2D( texture, slot ) { + } - const textureProperties = properties.get( texture ); + function DepthBuffer() { - if ( texture.isVideoTexture ) updateVideoTexture( texture ); + let locked = false; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + let currentDepthMask = null; + let currentDepthFunc = null; + let currentDepthClear = null; - const image = texture.image; + return { - if ( image === undefined ) { + setTest: function ( depthTest ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); + if ( depthTest ) { - } else if ( image.complete === false ) { + enable( 2929 ); - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); + } else { - } else { + disable( 2929 ); - uploadTexture( textureProperties, texture, slot ); - return; + } - } + }, - } + setMask: function ( depthMask ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); + if ( currentDepthMask !== depthMask && ! locked ) { - } + gl.depthMask( depthMask ); + currentDepthMask = depthMask; - function setTexture2DArray( texture, slot ) { + } - const textureProperties = properties.get( texture ); + }, - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + setFunc: function ( depthFunc ) { - uploadTexture( textureProperties, texture, slot ); - return; + if ( currentDepthFunc !== depthFunc ) { - } + if ( depthFunc ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); + switch ( depthFunc ) { - } + case NeverDepth: - function setTexture3D( texture, slot ) { + gl.depthFunc( 512 ); + break; - const textureProperties = properties.get( texture ); + case AlwaysDepth: - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + gl.depthFunc( 519 ); + break; - uploadTexture( textureProperties, texture, slot ); - return; + case LessDepth: - } + gl.depthFunc( 513 ); + break; - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); + case LessEqualDepth: - } + gl.depthFunc( 515 ); + break; - function setTextureCube( texture, slot ) { + case EqualDepth: - const textureProperties = properties.get( texture ); + gl.depthFunc( 514 ); + break; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + case GreaterEqualDepth: - uploadCubeTexture( textureProperties, texture, slot ); - return; + gl.depthFunc( 518 ); + break; - } + case GreaterDepth: - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + gl.depthFunc( 516 ); + break; - } + case NotEqualDepth: - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; + gl.depthFunc( 517 ); + break; - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, + default: - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; + gl.depthFunc( 515 ); - function setTextureParameters( textureType, texture, supportsMips ) { + } - if ( supportsMips ) { + } else { - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); + gl.depthFunc( 515 ); - if ( textureType === 32879 || textureType === 35866 ) { + } - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); + currentDepthFunc = depthFunc; - } + } - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); + }, - } else { + setLocked: function ( lock ) { - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); + locked = lock; - if ( textureType === 32879 || textureType === 35866 ) { + }, - _gl.texParameteri( textureType, 32882, 33071 ); + setClear: function ( depth ) { - } + if ( currentDepthClear !== depth ) { - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { + gl.clearDepth( depth ); + currentDepthClear = depth; - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); + } - } + }, - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); + reset: function () { - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { + locked = false; - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; } - } - - if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + }; - if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 - if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only + } - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + function StencilBuffer() { - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; + let locked = false; - } + let currentStencilMask = null; + let currentStencilFunc = null; + let currentStencilRef = null; + let currentStencilFuncMask = null; + let currentStencilFail = null; + let currentStencilZFail = null; + let currentStencilZPass = null; + let currentStencilClear = null; - } + return { - } + setTest: function ( stencilTest ) { - function initTexture( textureProperties, texture ) { + if ( ! locked ) { - if ( textureProperties.__webglInit === undefined ) { + if ( stencilTest ) { - textureProperties.__webglInit = true; + enable( 2960 ); - texture.addEventListener( 'dispose', onTextureDispose ); + } else { - textureProperties.__webglTexture = _gl.createTexture(); + disable( 2960 ); - info.memory.textures ++; + } - } + } - } + }, - function uploadTexture( textureProperties, texture, slot ) { + setMask: function ( stencilMask ) { - let textureType = 3553; + if ( currentStencilMask !== stencilMask && ! locked ) { - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; + gl.stencilMask( stencilMask ); + currentStencilMask = stencilMask; - initTexture( textureProperties, texture ); + } - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); + }, - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); + setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilFuncMask !== stencilMask ) { - const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; - setTextureParameters( textureType, texture, supportsMips ); + } - let mipmap; - const mipmaps = texture.mipmaps; + }, - if ( texture.isDepthTexture ) { + setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - // populate depth texture with dummy data + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { - glInternalFormat = 6402; + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - if ( isWebGL2 ) { + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; - if ( texture.type === FloatType ) { + } - glInternalFormat = 36012; + }, - } else if ( texture.type === UnsignedIntType ) { + setLocked: function ( lock ) { - glInternalFormat = 33190; + locked = lock; - } else if ( texture.type === UnsignedInt248Type ) { + }, - glInternalFormat = 35056; + setClear: function ( stencil ) { - } else { + if ( currentStencilClear !== stencil ) { - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D + gl.clearStencil( stencil ); + currentStencilClear = stencil; } - } else { + }, - if ( texture.type === FloatType ) { + reset: function () { - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); + locked = false; - } + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; } - // validation checks for WebGL 1 - - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { + }; - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { + } - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); + // - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); + const colorBuffer = new ColorBuffer(); + const depthBuffer = new DepthBuffer(); + const stencilBuffer = new StencilBuffer(); - } + let enabledCapabilities = {}; - } + let currentBoundFramebuffers = {}; + let currentDrawbuffers = new WeakMap(); + let defaultDrawbuffers = []; - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { + let currentProgram = null; - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; + let currentBlendingEnabled = false; + let currentBlending = null; + let currentBlendEquation = null; + let currentBlendSrc = null; + let currentBlendDst = null; + let currentBlendEquationAlpha = null; + let currentBlendSrcAlpha = null; + let currentBlendDstAlpha = null; + let currentPremultipledAlpha = false; - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { + let currentFlipSided = null; + let currentCullFace = null; - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); + let currentLineWidth = null; - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); + let currentPolygonOffsetFactor = null; + let currentPolygonOffsetUnits = null; - } + const maxTextures = gl.getParameter( 35661 ); - } + let lineWidthAvailable = false; + let version = 0; + const glVersion = gl.getParameter( 7938 ); - // + if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); + version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 1.0 ); - } else if ( texture.isDataTexture ) { + } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 2.0 ); - if ( mipmaps.length > 0 && supportsMips ) { + } - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + let currentTextureSlot = null; + let currentBoundTextures = {}; - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + const scissorParam = gl.getParameter( 3088 ); + const viewportParam = gl.getParameter( 2978 ); - } + const currentScissor = new Vector4().fromArray( scissorParam ); + const currentViewport = new Vector4().fromArray( viewportParam ); - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + function createTexture( type, target, count ) { - } else { + const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. + const texture = gl.createTexture(); - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + gl.bindTexture( type, texture ); + gl.texParameteri( type, 10241, 9728 ); + gl.texParameteri( type, 10240, 9728 ); - } + for ( let i = 0; i < count; i ++ ) { - } else if ( texture.isCompressedTexture ) { + gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + } - mipmap = mipmaps[ i ]; + return texture; - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + } - if ( glFormat !== null ) { + const emptyTextures = {}; + emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); + emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + // init - } else { + colorBuffer.setClear( 0, 0, 0, 1 ); + depthBuffer.setClear( 1 ); + stencilBuffer.setClear( 0 ); - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); + enable( 2929 ); + depthBuffer.setFunc( LessEqualDepth ); - } + setFlipSided( false ); + setCullFace( CullFaceBack ); + enable( 2884 ); - } else { + setBlending( NoBlending ); - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + // - } + function enable( id ) { - } + if ( enabledCapabilities[ id ] !== true ) { - textureProperties.__maxMipLevel = mipmaps.length - 1; + gl.enable( id ); + enabledCapabilities[ id ] = true; - } else if ( texture.isDataTexture2DArray ) { + } - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + } - } else if ( texture.isDataTexture3D ) { + function disable( id ) { - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + if ( enabledCapabilities[ id ] !== false ) { - } else { + gl.disable( id ); + enabledCapabilities[ id ] = false; - // regular Texture (image, video, canvas) + } - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + } - if ( mipmaps.length > 0 && supportsMips ) { + function bindFramebuffer( target, framebuffer ) { - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( currentBoundFramebuffers[ target ] !== framebuffer ) { - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); + gl.bindFramebuffer( target, framebuffer ); - } + currentBoundFramebuffers[ target ] = framebuffer; - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + if ( isWebGL2 ) { - } else { + // 36009 is equivalent to 36160 - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; + if ( target === 36009 ) { - } + currentBoundFramebuffers[ 36160 ] = framebuffer; - } + } - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + if ( target === 36160 ) { - generateMipmap( textureType, texture, image.width, image.height ); + currentBoundFramebuffers[ 36009 ] = framebuffer; - } + } - textureProperties.__version = texture.version; + } - if ( texture.onUpdate ) texture.onUpdate( texture ); + return true; - } + } - function uploadCubeTexture( textureProperties, texture, slot ) { + return false; - if ( texture.image.length !== 6 ) return; + } - initTexture( textureProperties, texture ); + function drawBuffers( renderTarget, framebuffer ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + let drawBuffers = defaultDrawbuffers; - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); + let needsUpdate = false; - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); + if ( renderTarget ) { - const cubeImage = []; + drawBuffers = currentDrawbuffers.get( framebuffer ); - for ( let i = 0; i < 6; i ++ ) { + if ( drawBuffers === undefined ) { - if ( ! isCompressed && ! isDataTexture ) { + drawBuffers = []; + currentDrawbuffers.set( framebuffer, drawBuffers ); - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); + } - } else { + if ( renderTarget.isWebGLMultipleRenderTargets ) { - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + const textures = renderTarget.texture; - } + if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== 36064 ) { - } + for ( let i = 0, il = textures.length; i < il; i ++ ) { - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + drawBuffers[ i ] = 36064 + i; - setTextureParameters( 34067, texture, supportsMips ); + } - let mipmaps; + drawBuffers.length = textures.length; - if ( isCompressed ) { + needsUpdate = true; - for ( let i = 0; i < 6; i ++ ) { + } - mipmaps = cubeImage[ i ].mipmaps; + } else { - for ( let j = 0; j < mipmaps.length; j ++ ) { + if ( drawBuffers[ 0 ] !== 36064 ) { - const mipmap = mipmaps[ j ]; + drawBuffers[ 0 ] = 36064; - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + needsUpdate = true; - if ( glFormat !== null ) { + } - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } - } else { + } else { - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); + if ( drawBuffers[ 0 ] !== 1029 ) { - } + drawBuffers[ 0 ] = 1029; - } else { + needsUpdate = true; - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } - } + } - } + if ( needsUpdate ) { - } + if ( capabilities.isWebGL2 ) { - textureProperties.__maxMipLevel = mipmaps.length - 1; + gl.drawBuffers( drawBuffers ); - } else { + } else { - mipmaps = texture.mipmaps; + extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers ); - for ( let i = 0; i < 6; i ++ ) { + } - if ( isDataTexture ) { + } - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - for ( let j = 0; j < mipmaps.length; j ++ ) { + } - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; + function useProgram( program ) { - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); + if ( currentProgram !== program ) { - } + gl.useProgram( program ); - } else { + currentProgram = program; - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); + return true; - for ( let j = 0; j < mipmaps.length; j ++ ) { + } - const mipmap = mipmaps[ j ]; + return false; - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); + } - } + const equationToGL = { + [ AddEquation ]: 32774, + [ SubtractEquation ]: 32778, + [ ReverseSubtractEquation ]: 32779 + }; - } + if ( isWebGL2 ) { - } + equationToGL[ MinEquation ] = 32775; + equationToGL[ MaxEquation ] = 32776; - textureProperties.__maxMipLevel = mipmaps.length; + } else { - } + const extension = extensions.get( 'EXT_blend_minmax' ); - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + if ( extension !== null ) { - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); + equationToGL[ MinEquation ] = extension.MIN_EXT; + equationToGL[ MaxEquation ] = extension.MAX_EXT; } - textureProperties.__version = texture.version; + } - if ( texture.onUpdate ) texture.onUpdate( texture ); + const factorToGL = { + [ ZeroFactor ]: 0, + [ OneFactor ]: 1, + [ SrcColorFactor ]: 768, + [ SrcAlphaFactor ]: 770, + [ SrcAlphaSaturateFactor ]: 776, + [ DstColorFactor ]: 774, + [ DstAlphaFactor ]: 772, + [ OneMinusSrcColorFactor ]: 769, + [ OneMinusSrcAlphaFactor ]: 771, + [ OneMinusDstColorFactor ]: 775, + [ OneMinusDstAlphaFactor ]: 773 + }; - } + function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - // Render targets + if ( blending === NoBlending ) { - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { + if ( currentBlendingEnabled === true ) { - const texture = renderTarget.texture; + disable( 3042 ); + currentBlendingEnabled = false; - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + } - if ( textureTarget === 32879 || textureTarget === 35866 ) { + return; - state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); + } - } else { + if ( currentBlendingEnabled === false ) { - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + enable( 3042 ); + currentBlendingEnabled = true; } - state.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); - state.bindFramebuffer( 36160, null ); + if ( blending !== CustomBlending ) { - } + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { + if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - _gl.bindRenderbuffer( 36161, renderbuffer ); + gl.blendEquation( 32774 ); - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; - let glInternalFormat = 33189; + } - if ( isMultisample ) { + if ( premultipliedAlpha ) { - const depthTexture = renderTarget.depthTexture; + switch ( blending ) { - if ( depthTexture && depthTexture.isDepthTexture ) { + case NormalBlending: + gl.blendFuncSeparate( 1, 771, 1, 771 ); + break; - if ( depthTexture.type === FloatType ) { + case AdditiveBlending: + gl.blendFunc( 1, 1 ); + break; - glInternalFormat = 36012; + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 769, 0, 1 ); + break; - } else if ( depthTexture.type === UnsignedIntType ) { + case MultiplyBlending: + gl.blendFuncSeparate( 0, 768, 0, 770 ); + break; - glInternalFormat = 33190; + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; } - } - - const samples = getRenderTargetSamples( renderTarget ); - - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + } else { - } else { + switch ( blending ) { - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + case NormalBlending: + gl.blendFuncSeparate( 770, 771, 1, 771 ); + break; - } + case AdditiveBlending: + gl.blendFunc( 770, 1 ); + break; - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 769, 0, 1 ); + break; - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + case MultiplyBlending: + gl.blendFunc( 0, 768 ); + break; - if ( isMultisample ) { + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - const samples = getRenderTargetSamples( renderTarget ); + } - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); + } - } else { + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; } + return; - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); + } - } else { + // custom blending - const texture = renderTarget.texture; + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - if ( isMultisample ) { + gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - const samples = getRenderTargetSamples( renderTarget ); + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + } - } else { + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - } + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; } - _gl.bindRenderbuffer( 36161, null ); + currentBlending = blending; + currentPremultipledAlpha = null; } - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { + function setMaterial( material, frontFaceCW ) { - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); + material.side === DoubleSide + ? disable( 2884 ) + : enable( 2884 ); - state.bindFramebuffer( 36160, framebuffer ); + let flipSided = ( material.side === BackSide ); + if ( frontFaceCW ) flipSided = ! flipSided; - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { + setFlipSided( flipSided ); - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); + ( material.blending === NormalBlending && material.transparent === false ) + ? setBlending( NoBlending ) + : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - } + depthBuffer.setFunc( material.depthFunc ); + depthBuffer.setTest( material.depthTest ); + depthBuffer.setMask( material.depthWrite ); + colorBuffer.setMask( material.colorWrite ); - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { + const stencilWrite = material.stencilWrite; + stencilBuffer.setTest( stencilWrite ); + if ( stencilWrite ) { - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; + stencilBuffer.setMask( material.stencilWriteMask ); + stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); + stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); } - setTexture2D( renderTarget.depthTexture, 0 ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + material.alphaToCoverage === true + ? enable( 32926 ) + : disable( 32926 ); - if ( renderTarget.depthTexture.format === DepthFormat ) { + } - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); + // - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + function setFlipSided( flipSided ) { - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); + if ( currentFlipSided !== flipSided ) { - } else { + if ( flipSided ) { - throw new Error( 'Unknown depthTexture format' ); + gl.frontFace( 2304 ); + + } else { + + gl.frontFace( 2305 ); + + } + + currentFlipSided = flipSided; } } - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { - - const renderTargetProperties = properties.get( renderTarget ); + function setCullFace( cullFace ) { - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + if ( cullFace !== CullFaceNone ) { - if ( renderTarget.depthTexture ) { + enable( 2884 ); - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); + if ( cullFace !== currentCullFace ) { - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + if ( cullFace === CullFaceBack ) { - } else { + gl.cullFace( 1029 ); - if ( isCube ) { + } else if ( cullFace === CullFaceFront ) { - renderTargetProperties.__webglDepthbuffer = []; + gl.cullFace( 1028 ); - for ( let i = 0; i < 6; i ++ ) { + } else { - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); + gl.cullFace( 1032 ); } - } else { + } - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); + } else { - } + disable( 2884 ); } - state.bindFramebuffer( 36160, null ); + currentCullFace = cullFace; } - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { - - const texture = renderTarget.texture; + function setLineWidth( width ) { - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); + if ( width !== currentLineWidth ) { - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + if ( lineWidthAvailable ) gl.lineWidth( width ); - textureProperties.__webglTexture = _gl.createTexture(); - textureProperties.__version = texture.version; + currentLineWidth = width; - info.memory.textures ++; + } - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; + } - // Handles WebGL2 RGBFormat fallback - #18858 + function setPolygonOffset( polygonOffset, factor, units ) { - if ( isWebGL2 && texture.format === RGBFormat && ( texture.type === FloatType || texture.type === HalfFloatType ) ) { + if ( polygonOffset ) { - texture.format = RGBAFormat; + enable( 32823 ); - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); + if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - } + gl.polygonOffset( factor, units ); - // Setup framebuffer + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; - if ( isCube ) { + } - renderTargetProperties.__webglFramebuffer = []; + } else { - for ( let i = 0; i < 6; i ++ ) { + disable( 32823 ); - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + } - } + } - } else { + function setScissorTest( scissorTest ) { - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + if ( scissorTest ) { - if ( isMultisample ) { + enable( 3089 ); - if ( isWebGL2 ) { + } else { - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + disable( 3089 ); - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); + } - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + } - state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); + // texture - if ( renderTarget.depthBuffer ) { + function activeTexture( webglSlot ) { - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); + if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - } + if ( currentTextureSlot !== webglSlot ) { - state.bindFramebuffer( 36160, null ); + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; + } - } else { + } - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + function bindTexture( webglType, webglTexture ) { - } + if ( currentTextureSlot === null ) { - } + activeTexture(); } - // Setup color buffer - - if ( isCube ) { + let boundTexture = currentBoundTextures[ currentTextureSlot ]; - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, texture, supportsMips ); + if ( boundTexture === undefined ) { - for ( let i = 0; i < 6; i ++ ) { + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); + } - } + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - generateMipmap( 34067, texture, renderTarget.width, renderTarget.height ); + boundTexture.type = webglType; + boundTexture.texture = webglTexture; - } + } - state.bindTexture( 34067, null ); + } - } else { + function unbindTexture() { - let glTextureType = 3553; + const boundTexture = currentBoundTextures[ currentTextureSlot ]; - if ( isRenderTarget3D ) { + if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - // Render targets containing layers, i.e: Texture 3D and 2d arrays + gl.bindTexture( boundTexture.type, null ); - if ( isWebGL2 ) { + boundTexture.type = undefined; + boundTexture.texture = undefined; - const isTexture3D = texture.isDataTexture3D; - glTextureType = isTexture3D ? 32879 : 35866; + } - } else { + } - console.warn( 'THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.' ); + function compressedTexImage2D() { - } + try { - } + gl.compressedTexImage2D.apply( gl, arguments ); - state.bindTexture( glTextureType, textureProperties.__webglTexture ); - setTextureParameters( glTextureType, texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, glTextureType ); + } catch ( error ) { - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + console.error( 'THREE.WebGLState:', error ); - generateMipmap( 3553, texture, renderTarget.width, renderTarget.height ); + } - } + } - state.bindTexture( 3553, null ); + function texSubImage2D() { - } + try { - // Setup depth and stencil buffers + gl.texSubImage2D.apply( gl, arguments ); - if ( renderTarget.depthBuffer ) { + } catch ( error ) { - setupDepthRenderbuffer( renderTarget ); + console.error( 'THREE.WebGLState:', error ); } } - function updateRenderTargetMipmap( renderTarget ) { - - const texture = renderTarget.texture; + function texSubImage3D() { - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; + try { - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + gl.texSubImage3D.apply( gl, arguments ); - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; + } catch ( error ) { - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); + console.error( 'THREE.WebGLState:', error ); } } - function updateMultisampleRenderTarget( renderTarget ) { + function compressedTexSubImage2D() { - if ( renderTarget.isWebGLMultisampleRenderTarget ) { + try { - if ( isWebGL2 ) { + gl.compressedTexSubImage2D.apply( gl, arguments ); - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; + } catch ( error ) { - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; + console.error( 'THREE.WebGLState:', error ); - const renderTargetProperties = properties.get( renderTarget ); + } - state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); + } - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); + function texStorage2D() { - state.bindFramebuffer( 36008, null ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); + try { - } else { + gl.texStorage2D.apply( gl, arguments ); - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); } } - function getRenderTargetSamples( renderTarget ) { - - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; - - } - - function updateVideoTexture( texture ) { + function texStorage3D() { - const frame = info.render.frame; + try { - // Check the last frame we updated the VideoTexture + gl.texStorage3D.apply( gl, arguments ); - if ( _videoTextures.get( texture ) !== frame ) { + } catch ( error ) { - _videoTextures.set( texture, frame ); - texture.update(); + console.error( 'THREE.WebGLState:', error ); } } - // backwards compatibility - - let warnedTexture2D = false; - let warnedTextureCube = false; - - function safeSetTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { + function texImage2D() { - if ( warnedTexture2D === false ) { + try { - console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' ); - warnedTexture2D = true; + gl.texImage2D.apply( gl, arguments ); - } + } catch ( error ) { - texture = texture.texture; + console.error( 'THREE.WebGLState:', error ); } - setTexture2D( texture, slot ); - } - function safeSetTextureCube( texture, slot ) { + function texImage3D() { - if ( texture && texture.isWebGLCubeRenderTarget ) { + try { - if ( warnedTextureCube === false ) { - - console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' ); - warnedTextureCube = true; + gl.texImage3D.apply( gl, arguments ); - } + } catch ( error ) { - texture = texture.texture; + console.error( 'THREE.WebGLState:', error ); } - - setTextureCube( texture, slot ); - } // - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; - - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; - - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; - - } + function scissor( scissor ) { - function WebGLUtils( gl, extensions, capabilities ) { + if ( currentScissor.equals( scissor ) === false ) { - const isWebGL2 = capabilities.isWebGL2; + gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); + currentScissor.copy( scissor ); - function convert( p ) { + } - let extension; + } - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; + function viewport( viewport ) { - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; + if ( currentViewport.equals( viewport ) === false ) { - if ( p === HalfFloatType ) { + gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); + currentViewport.copy( viewport ); - if ( isWebGL2 ) return 5131; + } - extension = extensions.get( 'OES_texture_half_float' ); + } - if ( extension !== null ) { + // - return extension.HALF_FLOAT_OES; + function reset() { - } else { + // reset state - return null; + gl.disable( 3042 ); + gl.disable( 2884 ); + gl.disable( 2929 ); + gl.disable( 32823 ); + gl.disable( 3089 ); + gl.disable( 2960 ); + gl.disable( 32926 ); - } + gl.blendEquation( 32774 ); + gl.blendFunc( 1, 0 ); + gl.blendFuncSeparate( 1, 0, 1, 0 ); - } + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; + gl.depthMask( true ); + gl.depthFunc( 513 ); + gl.clearDepth( 1 ); - // WebGL2 formats. + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( 519, 0, 0xffffffff ); + gl.stencilOp( 7680, 7680, 7680 ); + gl.clearStencil( 0 ); - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; + gl.cullFace( 1029 ); + gl.frontFace( 2305 ); - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { + gl.polygonOffset( 0, 0 ); - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + gl.activeTexture( 33984 ); - if ( extension !== null ) { + gl.bindFramebuffer( 36160, null ); - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + if ( isWebGL2 === true ) { - } else { + gl.bindFramebuffer( 36009, null ); + gl.bindFramebuffer( 36008, null ); - return null; + } - } + gl.useProgram( null ); - } + gl.lineWidth( 1 ); - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + // reset internals - if ( extension !== null ) { + enabledCapabilities = {}; - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + currentTextureSlot = null; + currentBoundTextures = {}; - } else { + currentBoundFramebuffers = {}; + currentDrawbuffers = new WeakMap(); + defaultDrawbuffers = []; - return null; + currentProgram = null; - } + currentBlendingEnabled = false; + currentBlending = null; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentPremultipledAlpha = false; - } + currentFlipSided = null; + currentCullFace = null; - if ( p === RGB_ETC1_Format ) { + currentLineWidth = null; - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); + currentPolygonOffsetFactor = null; + currentPolygonOffsetUnits = null; - if ( extension !== null ) { + currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); - return extension.COMPRESSED_RGB_ETC1_WEBGL; + colorBuffer.reset(); + depthBuffer.reset(); + stencilBuffer.reset(); - } else { + } - return null; + return { - } + buffers: { + color: colorBuffer, + depth: depthBuffer, + stencil: stencilBuffer + }, - } + enable: enable, + disable: disable, - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { + bindFramebuffer: bindFramebuffer, + drawBuffers: drawBuffers, - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); + useProgram: useProgram, - if ( extension !== null ) { + setBlending: setBlending, + setMaterial: setMaterial, - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; + setFlipSided: setFlipSided, + setCullFace: setCullFace, - } + setLineWidth: setLineWidth, + setPolygonOffset: setPolygonOffset, - } + setScissorTest: setScissorTest, - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { + activeTexture: activeTexture, + bindTexture: bindTexture, + unbindTexture: unbindTexture, + compressedTexImage2D: compressedTexImage2D, + texImage2D: texImage2D, + texImage3D: texImage3D, - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); + texStorage2D: texStorage2D, + texStorage3D: texStorage3D, + texSubImage2D: texSubImage2D, + texSubImage3D: texSubImage3D, + compressedTexSubImage2D: compressedTexSubImage2D, - if ( extension !== null ) { + scissor: scissor, + viewport: viewport, - // TODO Complete? + reset: reset - return p; + }; - } else { + } - return null; + function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - } + const isWebGL2 = capabilities.isWebGL2; + const maxTextures = capabilities.maxTextures; + const maxCubemapSize = capabilities.maxCubemapSize; + const maxTextureSize = capabilities.maxTextureSize; + const maxSamples = capabilities.maxSamples; + const multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null; + const supportsInvalidateFramebuffer = /OculusBrowser/g.test( navigator.userAgent ); - } + const _videoTextures = new WeakMap(); + let _canvas; - if ( p === RGBA_BPTC_Format ) { + const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source - extension = extensions.get( 'EXT_texture_compression_bptc' ); + // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, + // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! + // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - if ( extension !== null ) { + let useOffscreenCanvas = false; - // TODO Complete? + try { - return p; + useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' + // eslint-disable-next-line compat/compat + && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; - } else { + } catch ( err ) { - return null; + // Ignore any errors - } + } - } + function createCanvas( width, height ) { - if ( p === UnsignedInt248Type ) { + // Use OffscreenCanvas when available. Specially needed in web workers - if ( isWebGL2 ) return 34042; + return useOffscreenCanvas ? + // eslint-disable-next-line compat/compat + new OffscreenCanvas( width, height ) : createElementNS( 'canvas' ); - extension = extensions.get( 'WEBGL_depth_texture' ); + } - if ( extension !== null ) { + function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - return extension.UNSIGNED_INT_24_8_WEBGL; + let scale = 1; - } else { + // handle case if texture exceeds max size - return null; + if ( image.width > maxSize || image.height > maxSize ) { - } + scale = maxSize / Math.max( image.width, image.height ); } - } + // only perform resize if necessary - return { convert: convert }; + if ( scale < 1 || needsPowerOfTwo === true ) { - } + // only perform resize for certain image types - class ArrayCamera extends PerspectiveCamera { + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - constructor( array = [] ) { + const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; - super(); + const width = floor( scale * image.width ); + const height = floor( scale * image.height ); - this.cameras = array; + if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - } + // cube textures can't reuse the same canvas - } + const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - ArrayCamera.prototype.isArrayCamera = true; + canvas.width = width; + canvas.height = height; - class Group extends Object3D { + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - constructor() { + console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - super(); + return canvas; - this.type = 'Group'; + } else { - } + if ( 'data' in image ) { - } + console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - Group.prototype.isGroup = true; + } - const _moveEvent = { type: 'move' }; + return image; - class WebXRController { + } - constructor() { + } - this._targetRay = null; - this._grip = null; - this._hand = null; + return image; } - getHandSpace() { + function isPowerOfTwo$1( image ) { - if ( this._hand === null ) { + return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; + } - this._hand.joints = {}; - this._hand.inputState = { pinching: false }; + function textureNeedsPowerOfTwo( texture ) { - } + if ( isWebGL2 ) return false; - return this._hand; + return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || + ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } - getTargetRaySpace() { + function textureNeedsGenerateMipmaps( texture, supportsMips ) { - if ( this._targetRay === null ) { + return texture.generateMipmaps && supportsMips && + texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - this._targetRay.hasLinearVelocity = false; - this._targetRay.linearVelocity = new Vector3(); - this._targetRay.hasAngularVelocity = false; - this._targetRay.angularVelocity = new Vector3(); + } - } + function generateMipmap( target ) { - return this._targetRay; + _gl.generateMipmap( target ); } - getGripSpace() { + function getInternalFormat( internalFormatName, glFormat, glType, encoding, isVideoTexture = false ) { - if ( this._grip === null ) { + if ( isWebGL2 === false ) return glFormat; - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - this._grip.hasLinearVelocity = false; - this._grip.linearVelocity = new Vector3(); - this._grip.hasAngularVelocity = false; - this._grip.angularVelocity = new Vector3(); + if ( internalFormatName !== null ) { + + if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; + + console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); } - return this._grip; + let internalFormat = glFormat; - } + if ( glFormat === 6403 ) { - dispatchEvent( event ) { + if ( glType === 5126 ) internalFormat = 33326; + if ( glType === 5131 ) internalFormat = 33325; + if ( glType === 5121 ) internalFormat = 33321; - if ( this._targetRay !== null ) { + } - this._targetRay.dispatchEvent( event ); + if ( glFormat === 33319 ) { + + if ( glType === 5126 ) internalFormat = 33328; + if ( glType === 5131 ) internalFormat = 33327; + if ( glType === 5121 ) internalFormat = 33323; } - if ( this._grip !== null ) { + if ( glFormat === 6408 ) { - this._grip.dispatchEvent( event ); + if ( glType === 5126 ) internalFormat = 34836; + if ( glType === 5131 ) internalFormat = 34842; + if ( glType === 5121 ) internalFormat = ( encoding === sRGBEncoding && isVideoTexture === false ) ? 35907 : 32856; + if ( glType === 32819 ) internalFormat = 32854; + if ( glType === 32820 ) internalFormat = 32855; } - if ( this._hand !== null ) { + if ( internalFormat === 33325 || internalFormat === 33326 || + internalFormat === 33327 || internalFormat === 33328 || + internalFormat === 34842 || internalFormat === 34836 ) { - this._hand.dispatchEvent( event ); + extensions.get( 'EXT_color_buffer_float' ); } - return this; + return internalFormat; } - disconnect( inputSource ) { + function getMipLevels( texture, image, supportsMips ) { - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { - if ( this._targetRay !== null ) { + return Math.log2( Math.max( image.width, image.height ) ) + 1; - this._targetRay.visible = false; + } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { - } + // user-defined mipmaps - if ( this._grip !== null ) { + return texture.mipmaps.length; - this._grip.visible = false; + } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { - } + return image.mipmaps.length; - if ( this._hand !== null ) { + } else { - this._hand.visible = false; + // texture without mipmaps (only base level) - } + return 1; - return this; + } } - update( inputSource, frame, referenceSpace ) { - - let inputPose = null; - let gripPose = null; - let handPose = null; + // Fallback filters for non-power-of-2 textures - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; + function filterFallback( f ) { - if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { + if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - if ( targetRay !== null ) { + return 9728; - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); + } - if ( inputPose !== null ) { + return 9729; - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); + } - if ( inputPose.linearVelocity ) { + // - targetRay.hasLinearVelocity = true; - targetRay.linearVelocity.copy( inputPose.linearVelocity ); + function onTextureDispose( event ) { - } else { + const texture = event.target; - targetRay.hasLinearVelocity = false; + texture.removeEventListener( 'dispose', onTextureDispose ); - } + deallocateTexture( texture ); - if ( inputPose.angularVelocity ) { + if ( texture.isVideoTexture ) { - targetRay.hasAngularVelocity = true; - targetRay.angularVelocity.copy( inputPose.angularVelocity ); + _videoTextures.delete( texture ); - } else { + } - targetRay.hasAngularVelocity = false; + } - } + function onRenderTargetDispose( event ) { - this.dispatchEvent( _moveEvent ); + const renderTarget = event.target; - } + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - } + deallocateRenderTarget( renderTarget ); - if ( hand && inputSource.hand ) { + } - handPose = true; + // - for ( const inputjoint of inputSource.hand.values() ) { + function deallocateTexture( texture ) { - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputjoint, referenceSpace ); + const textureProperties = properties.get( texture ); - if ( hand.joints[ inputjoint.jointName ] === undefined ) { + if ( textureProperties.__webglInit === undefined ) return; - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - hand.joints[ inputjoint.jointName ] = joint; - // ?? - hand.add( joint ); + // check if it's necessary to remove the WebGLTexture object - } + const source = texture.source; + const webglTextures = _sources.get( source ); - const joint = hand.joints[ inputjoint.jointName ]; + if ( webglTextures ) { - if ( jointPose !== null ) { + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; + webglTexture.usedTimes --; - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; + // the WebGLTexture object is not used anymore, remove it - } + if ( webglTexture.usedTimes === 0 ) { - joint.visible = jointPose !== null; + deleteTexture( texture ); - } + } - // Custom events + // remove the weak map entry if no WebGLTexture uses the source anymore - // Check pinchz - const indexTip = hand.joints[ 'index-finger-tip' ]; - const thumbTip = hand.joints[ 'thumb-tip' ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); + if ( Object.keys( webglTextures ).length === 0 ) { - const distanceToPinch = 0.02; - const threshold = 0.005; + _sources.delete( source ); - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { + } - hand.inputState.pinching = false; - this.dispatchEvent( { - type: 'pinchend', - handedness: inputSource.handedness, - target: this - } ); + } - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { + properties.remove( texture ); - hand.inputState.pinching = true; - this.dispatchEvent( { - type: 'pinchstart', - handedness: inputSource.handedness, - target: this - } ); + } - } + function deleteTexture( texture ) { - } else { + const textureProperties = properties.get( texture ); + _gl.deleteTexture( textureProperties.__webglTexture ); - if ( grip !== null && inputSource.gripSpace ) { + const source = texture.source; + const webglTextures = _sources.get( source ); + delete webglTextures[ textureProperties.__cacheKey ]; - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); + info.memory.textures --; - if ( gripPose !== null ) { + } - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); + function deallocateRenderTarget( renderTarget ) { - if ( gripPose.linearVelocity ) { + const texture = renderTarget.texture; - grip.hasLinearVelocity = true; - grip.linearVelocity.copy( gripPose.linearVelocity ); + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - } else { + if ( textureProperties.__webglTexture !== undefined ) { - grip.hasLinearVelocity = false; + _gl.deleteTexture( textureProperties.__webglTexture ); - } + info.memory.textures --; - if ( gripPose.angularVelocity ) { + } - grip.hasAngularVelocity = true; - grip.angularVelocity.copy( gripPose.angularVelocity ); + if ( renderTarget.depthTexture ) { - } else { + renderTarget.depthTexture.dispose(); - grip.hasAngularVelocity = false; + } - } + if ( renderTarget.isWebGLCubeRenderTarget ) { - } + for ( let i = 0; i < 6; i ++ ) { - } + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } - } + } else { - if ( targetRay !== null ) { - - targetRay.visible = ( inputPose !== null ); + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); + if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); + if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); + if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } - if ( grip !== null ) { + if ( renderTarget.isWebGLMultipleRenderTargets ) { - grip.visible = ( gripPose !== null ); + for ( let i = 0, il = texture.length; i < il; i ++ ) { - } + const attachmentProperties = properties.get( texture[ i ] ); - if ( hand !== null ) { + if ( attachmentProperties.__webglTexture ) { - hand.visible = ( handPose !== null ); + _gl.deleteTexture( attachmentProperties.__webglTexture ); + + info.memory.textures --; + + } + + properties.remove( texture[ i ] ); + + } } - return this; + properties.remove( texture ); + properties.remove( renderTarget ); } - } + // - class WebXRManager extends EventDispatcher$1 { + let textureUnits = 0; - constructor( renderer, gl ) { + function resetTextureUnits() { - super(); + textureUnits = 0; - const scope = this; - const state = renderer.state; + } - let session = null; + function allocateTextureUnit() { - let framebufferScaleFactor = 1.0; + const textureUnit = textureUnits; - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; + if ( textureUnit >= maxTextures ) { - let pose = null; + console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - const controllers = []; - const inputSourcesMap = new Map(); + } - // + textureUnits += 1; - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); + return textureUnit; - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); + } - const cameras = [ cameraL, cameraR ]; + function getTextureCacheKey( texture ) { - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); + const array = []; - let _currentDepthNear = null; - let _currentDepthFar = null; + array.push( texture.wrapS ); + array.push( texture.wrapT ); + array.push( texture.magFilter ); + array.push( texture.minFilter ); + array.push( texture.anisotropy ); + array.push( texture.internalFormat ); + array.push( texture.format ); + array.push( texture.type ); + array.push( texture.generateMipmaps ); + array.push( texture.premultiplyAlpha ); + array.push( texture.flipY ); + array.push( texture.unpackAlignment ); + array.push( texture.encoding ); - // + return array.join(); - this.enabled = false; + } - this.isPresenting = false; + // - this.getController = function ( index ) { + function setTexture2D( texture, slot ) { - let controller = controllers[ index ]; + const textureProperties = properties.get( texture ); - if ( controller === undefined ) { + if ( texture.isVideoTexture ) updateVideoTexture( texture ); - controller = new WebXRController(); - controllers[ index ] = controller; + if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { - } + const image = texture.image; - return controller.getTargetRaySpace(); + if ( image === null ) { - }; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' ); - this.getControllerGrip = function ( index ) { + } else if ( image.complete === false ) { - let controller = controllers[ index ]; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - if ( controller === undefined ) { + } else { - controller = new WebXRController(); - controllers[ index ] = controller; + uploadTexture( textureProperties, texture, slot ); + return; } - return controller.getGripSpace(); - - }; + } - this.getHand = function ( index ) { + state.activeTexture( 33984 + slot ); + state.bindTexture( 3553, textureProperties.__webglTexture ); - let controller = controllers[ index ]; + } - if ( controller === undefined ) { + function setTexture2DArray( texture, slot ) { - controller = new WebXRController(); - controllers[ index ] = controller; + const textureProperties = properties.get( texture ); - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - return controller.getHandSpace(); + uploadTexture( textureProperties, texture, slot ); + return; - }; + } - // + state.activeTexture( 33984 + slot ); + state.bindTexture( 35866, textureProperties.__webglTexture ); - function onSessionEvent( event ) { + } - const controller = inputSourcesMap.get( event.inputSource ); + function setTexture3D( texture, slot ) { - if ( controller ) { + const textureProperties = properties.get( texture ); - controller.dispatchEvent( { type: event.type, data: event.inputSource } ); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - } + uploadTexture( textureProperties, texture, slot ); + return; } - function onSessionEnd() { + state.activeTexture( 33984 + slot ); + state.bindTexture( 32879, textureProperties.__webglTexture ); - inputSourcesMap.forEach( function ( controller, inputSource ) { + } - controller.disconnect( inputSource ); + function setTextureCube( texture, slot ) { - } ); + const textureProperties = properties.get( texture ); - inputSourcesMap.clear(); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - _currentDepthNear = null; - _currentDepthFar = null; + uploadCubeTexture( textureProperties, texture, slot ); + return; - // restore framebuffer/rendering state + } - state.bindXRFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - // + } - animation.stop(); + const wrappingToGL = { + [ RepeatWrapping ]: 10497, + [ ClampToEdgeWrapping ]: 33071, + [ MirroredRepeatWrapping ]: 33648 + }; - scope.isPresenting = false; + const filterToGL = { + [ NearestFilter ]: 9728, + [ NearestMipmapNearestFilter ]: 9984, + [ NearestMipmapLinearFilter ]: 9986, - scope.dispatchEvent( { type: 'sessionend' } ); + [ LinearFilter ]: 9729, + [ LinearMipmapNearestFilter ]: 9985, + [ LinearMipmapLinearFilter ]: 9987 + }; - } + function setTextureParameters( textureType, texture, supportsMips ) { - this.setFramebufferScaleFactor = function ( value ) { + if ( supportsMips ) { - framebufferScaleFactor = value; + _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); + _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - if ( scope.isPresenting === true ) { + if ( textureType === 32879 || textureType === 35866 ) { - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); + _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); } - }; + _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); + _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - this.setReferenceSpaceType = function ( value ) { + } else { - referenceSpaceType = value; + _gl.texParameteri( textureType, 10242, 33071 ); + _gl.texParameteri( textureType, 10243, 33071 ); - if ( scope.isPresenting === true ) { + if ( textureType === 32879 || textureType === 35866 ) { - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); + _gl.texParameteri( textureType, 32882, 33071 ); } - }; + if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - this.getReferenceSpace = function () { + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - return referenceSpace; + } - }; + _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - this.getSession = function () { + if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - return session; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - }; + } - this.setSession = async function ( value ) { + } - session = value; + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - if ( session !== null ) { + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - session.addEventListener( 'inputsourceschange', onInputSourcesChange ); + if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 + if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only - const attributes = gl.getContextAttributes(); + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - if ( attributes.xrCompatible !== true ) { + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; - await gl.makeXRCompatible(); + } - } + } - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; + } - // eslint-disable-next-line no-undef - const baseLayer = new XRWebGLLayer( session, gl, layerInit ); + function initTexture( textureProperties, texture ) { - session.updateRenderState( { baseLayer: baseLayer } ); + let forceUpload = false; - referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); + if ( textureProperties.__webglInit === undefined ) { - animation.setContext( session ); - animation.start(); + textureProperties.__webglInit = true; - scope.isPresenting = true; + texture.addEventListener( 'dispose', onTextureDispose ); - scope.dispatchEvent( { type: 'sessionstart' } ); + } - } + // create Source <-> WebGLTextures mapping if necessary - }; + const source = texture.source; + let webglTextures = _sources.get( source ); - function onInputSourcesChange( event ) { + if ( webglTextures === undefined ) { - const inputSources = session.inputSources; + webglTextures = {}; + _sources.set( source, webglTextures ); - // Assign inputSources to available controllers + } - for ( let i = 0; i < controllers.length; i ++ ) { + // check if there is already a WebGLTexture object for the given texture parameters - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); + const textureCacheKey = getTextureCacheKey( texture ); - } + if ( textureCacheKey !== textureProperties.__cacheKey ) { - // Notify disconnected + // if not, create a new instance of WebGLTexture - for ( let i = 0; i < event.removed.length; i ++ ) { + if ( webglTextures[ textureCacheKey ] === undefined ) { - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); + // create new entry - if ( controller ) { + webglTextures[ textureCacheKey ] = { + texture: _gl.createTexture(), + usedTimes: 0 + }; - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); + info.memory.textures ++; - } + // when a new instance of WebGLTexture was created, a texture upload is required + // even if the image contents are identical + + forceUpload = true; } - // Notify connected + webglTextures[ textureCacheKey ].usedTimes ++; - for ( let i = 0; i < event.added.length; i ++ ) { + // every time the texture cache key changes, it's necessary to check if an instance of + // WebGLTexture can be deleted in order to avoid a memory leak. - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; - if ( controller ) { + if ( webglTexture !== undefined ) { - controller.dispatchEvent( { type: 'connected', data: inputSource } ); + webglTextures[ textureProperties.__cacheKey ].usedTimes --; + + if ( webglTexture.usedTimes === 0 ) { + + deleteTexture( texture ); } } - } + // store references to cache key and WebGLTexture object - // + textureProperties.__cacheKey = textureCacheKey; + textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); + } - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { + return forceUpload; - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); + } - const ipd = cameraLPos.distanceTo( cameraRPos ); + function uploadTexture( textureProperties, texture, slot ) { - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; + let textureType = 3553; - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; + if ( texture.isDataArrayTexture ) textureType = 35866; + if ( texture.isData3DTexture ) textureType = 32879; - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; + state.activeTexture( 33984 + slot ); + state.bindTexture( textureType, textureProperties.__webglTexture ); - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + if ( source.version !== source.__currentVersion || forceUpload === true ) { - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; + let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + image = verifyColorSpace( texture, image ); - } + const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.encoding ); - function updateCamera( camera, parent ) { + let glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture ); - if ( parent === null ) { + setTextureParameters( textureType, texture, supportsMips ); - camera.matrixWorld.copy( camera.matrix ); + let mipmap; + const mipmaps = texture.mipmaps; - } else { + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( textureProperties.__version === undefined ); + const levels = getMipLevels( texture, image, supportsMips ); - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); + if ( texture.isDepthTexture ) { - } + // populate depth texture with dummy data - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + glInternalFormat = 6402; - } + if ( isWebGL2 ) { - this.getCamera = function ( camera ) { + if ( texture.type === FloatType ) { - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; + glInternalFormat = 36012; - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { + } else if ( texture.type === UnsignedIntType ) { - // Note that the new renderState won't apply until the next frame. See #18320 + glInternalFormat = 33190; - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); + } else if ( texture.type === UnsignedInt248Type ) { - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; + glInternalFormat = 35056; - } + } else { - const parent = camera.parent; - const cameras = cameraVR.cameras; + glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - updateCamera( cameraVR, parent ); + } - for ( let i = 0; i < cameras.length; i ++ ) { + } else { - updateCamera( cameras[ i ], parent ); + if ( texture.type === FloatType ) { - } + console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - // update camera and its children + } - camera.matrixWorld.copy( cameraVR.matrixWorld ); - camera.matrix.copy( cameraVR.matrix ); - camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + } - const children = camera.children; + // validation checks for WebGL 1 - for ( let i = 0, l = children.length; i < l; i ++ ) { + if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - children[ i ].updateMatrixWorld( true ); + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - } + console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - // update projection matrix for proper view frustum culling + texture.type = UnsignedShortType; + glType = utils.convert( texture.type ); - if ( cameras.length === 2 ) { + } - setProjectionFromUnion( cameraVR, cameraL, cameraR ); + } - } else { + if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - // assume single camera setup (AR) + // Depth stencil textures need the DEPTH_STENCIL internal format + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + glInternalFormat = 34041; - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedInt248Type ) { - } + console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - return cameraVR; + texture.type = UnsignedInt248Type; + glType = utils.convert( texture.type ); - }; + } - // Animation Loop + } - let onAnimationFrameCallback = null; + // - function onAnimationFrame( time, frame ) { + if ( useTexStorage && allocateMemory ) { - pose = frame.getViewerPose( referenceSpace ); + state.texStorage2D( 3553, 1, glInternalFormat, image.width, image.height ); - if ( pose !== null ) { + } else { - const views = pose.views; - const baseLayer = session.renderState.baseLayer; + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - state.bindXRFramebuffer( baseLayer.framebuffer ); + } - let cameraVRNeedsUpdate = false; + } else if ( texture.isDataTexture ) { - // check if it's necessary to rebuild cameraVR's camera list + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - if ( views.length !== cameraVR.cameras.length ) { + if ( mipmaps.length > 0 && supportsMips ) { - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; + if ( useTexStorage && allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - for ( let i = 0; i < views.length; i ++ ) { + } - const view = views[ i ]; - const viewport = baseLayer.getViewport( view ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - const camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); + mipmap = mipmaps[ i ]; - if ( i === 0 ) { + if ( useTexStorage ) { - cameraVR.matrix.copy( camera.matrix ); + state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - } + } else { - if ( cameraVRNeedsUpdate === true ) { + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - cameraVR.cameras.push( camera ); + } } - } + texture.generateMipmaps = false; - } + } else { - // + if ( useTexStorage ) { - const inputSources = session.inputSources; + if ( allocateMemory ) { - for ( let i = 0; i < controllers.length; i ++ ) { + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; + } - controller.update( inputSource, frame, referenceSpace ); + state.texSubImage2D( 3553, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); - } + } else { - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - } + } - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + } - this.setAnimationLoop = function ( callback ) { + } else if ( texture.isCompressedTexture ) { - onAnimationFrameCallback = callback; + if ( useTexStorage && allocateMemory ) { - }; + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - this.dispose = function () {}; + } - } + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - } + mipmap = mipmaps[ i ]; - function WebGLMaterials( properties ) { + if ( texture.format !== RGBAFormat ) { - function refreshFogUniforms( uniforms, fog ) { + if ( glFormat !== null ) { - uniforms.fogColor.value.copy( fog.color ); + if ( useTexStorage ) { - if ( fog.isFog ) { + state.compressedTexSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } else { - } else if ( fog.isFogExp2 ) { + state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - uniforms.fogDensity.value = fog.density; + } - } + } else { - } + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) { + } - if ( material.isMeshBasicMaterial ) { + } else { - refreshUniformsCommon( uniforms, material ); + if ( useTexStorage ) { - } else if ( material.isMeshLambertMaterial ) { + state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); + } else { - } else if ( material.isMeshToonMaterial ) { + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); + } - } else if ( material.isMeshPhongMaterial ) { + } - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); + } - } else if ( material.isMeshStandardMaterial ) { + } else if ( texture.isDataArrayTexture ) { - refreshUniformsCommon( uniforms, material ); + if ( useTexStorage ) { - if ( material.isMeshPhysicalMaterial ) { + if ( allocateMemory ) { - refreshUniformsPhysical( uniforms, material ); + state.texStorage3D( 35866, levels, glInternalFormat, image.width, image.height, image.depth ); - } else { + } - refreshUniformsStandard( uniforms, material ); + state.texSubImage3D( 35866, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - } + } else { - } else if ( material.isMeshMatcapMaterial ) { + state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); + } - } else if ( material.isMeshDepthMaterial ) { + } else if ( texture.isData3DTexture ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); + if ( useTexStorage ) { - } else if ( material.isMeshDistanceMaterial ) { + if ( allocateMemory ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); + state.texStorage3D( 32879, levels, glInternalFormat, image.width, image.height, image.depth ); - } else if ( material.isMeshNormalMaterial ) { + } - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); + state.texSubImage3D( 32879, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - } else if ( material.isLineBasicMaterial ) { + } else { - refreshUniformsLine( uniforms, material ); + state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - if ( material.isLineDashedMaterial ) { + } - refreshUniformsDash( uniforms, material ); + } else if ( texture.isFramebufferTexture ) { - } + if ( useTexStorage && allocateMemory ) { - } else if ( material.isPointsMaterial ) { + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - refreshUniformsPoints( uniforms, material, pixelRatio, height ); + } else { - } else if ( material.isSpriteMaterial ) { + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - refreshUniformsSprites( uniforms, material ); + } - } else if ( material.isShadowMaterial ) { + } else { - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + // regular Texture (image, video, canvas) - } else if ( material.isShaderMaterial ) { + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - material.uniformsNeedUpdate = false; // #15581 + if ( mipmaps.length > 0 && supportsMips ) { - } + if ( useTexStorage && allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - function refreshUniformsCommon( uniforms, material ) { + } - uniforms.opacity.value = material.opacity; + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - if ( material.color ) { + mipmap = mipmaps[ i ]; - uniforms.diffuse.value.copy( material.color ); + if ( useTexStorage ) { - } + state.texSubImage2D( 3553, i, 0, 0, glFormat, glType, mipmap ); - if ( material.emissive ) { + } else { - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); + state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - } + } - if ( material.map ) { + } - uniforms.map.value = material.map; + texture.generateMipmaps = false; - } + } else { - if ( material.alphaMap ) { + if ( useTexStorage ) { - uniforms.alphaMap.value = material.alphaMap; + if ( allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - if ( material.specularMap ) { + } - uniforms.specularMap.value = material.specularMap; + state.texSubImage2D( 3553, 0, 0, 0, glFormat, glType, image ); - } + } else { - const envMap = properties.get( material ).envMap; + state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - if ( envMap ) { + } - uniforms.envMap.value = envMap; + } - uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap._needsFlipEnvMap ) ? - 1 : 1; + } - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - const maxMipLevel = properties.get( envMap ).__maxMipLevel; + generateMipmap( textureType ); - if ( maxMipLevel !== undefined ) { + } - uniforms.maxMipLevel.value = maxMipLevel; + source.__currentVersion = source.version; - } + if ( texture.onUpdate ) texture.onUpdate( texture ); } - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; + textureProperties.__version = texture.version; - } + } - if ( material.aoMap ) { + function uploadCubeTexture( textureProperties, texture, slot ) { - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; + if ( texture.image.length !== 6 ) return; - } + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - let uvScaleMap; + if ( source.version !== source.__currentVersion || forceUpload === true ) { - if ( material.map ) { + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - uvScaleMap = material.map; + const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); + const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - } else if ( material.specularMap ) { + const cubeImage = []; - uvScaleMap = material.specularMap; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.displacementMap ) { + if ( ! isCompressed && ! isDataTexture ) { - uvScaleMap = material.displacementMap; + cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - } else if ( material.normalMap ) { + } else { - uvScaleMap = material.normalMap; + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - } else if ( material.bumpMap ) { + } - uvScaleMap = material.bumpMap; + cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); - } else if ( material.roughnessMap ) { + } - uvScaleMap = material.roughnessMap; + const image = cubeImage[ 0 ], + supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.encoding ), + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - } else if ( material.metalnessMap ) { + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( textureProperties.__version === undefined ); + let levels = getMipLevels( texture, image, supportsMips ); - uvScaleMap = material.metalnessMap; + setTextureParameters( 34067, texture, supportsMips ); - } else if ( material.alphaMap ) { + let mipmaps; - uvScaleMap = material.alphaMap; + if ( isCompressed ) { - } else if ( material.emissiveMap ) { + if ( useTexStorage && allocateMemory ) { - uvScaleMap = material.emissiveMap; + state.texStorage2D( 34067, levels, glInternalFormat, image.width, image.height ); - } else if ( material.clearcoatMap ) { + } - uvScaleMap = material.clearcoatMap; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.clearcoatNormalMap ) { + mipmaps = cubeImage[ i ].mipmaps; - uvScaleMap = material.clearcoatNormalMap; + for ( let j = 0; j < mipmaps.length; j ++ ) { - } else if ( material.clearcoatRoughnessMap ) { + const mipmap = mipmaps[ j ]; - uvScaleMap = material.clearcoatRoughnessMap; + if ( texture.format !== RGBAFormat ) { - } + if ( glFormat !== null ) { - if ( uvScaleMap !== undefined ) { + if ( useTexStorage ) { - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { + state.compressedTexSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - uvScaleMap = uvScaleMap.texture; + } else { - } + state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - if ( uvScaleMap.matrixAutoUpdate === true ) { + } - uvScaleMap.updateMatrix(); + } else { - } + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } - } + } else { - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map + if ( useTexStorage ) { - let uv2ScaleMap; + state.texSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - if ( material.aoMap ) { + } else { - uv2ScaleMap = material.aoMap; + state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } else if ( material.lightMap ) { + } - uv2ScaleMap = material.lightMap; + } - } + } - if ( uv2ScaleMap !== undefined ) { + } - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { + } else { - uv2ScaleMap = uv2ScaleMap.texture; + mipmaps = texture.mipmaps; - } + if ( useTexStorage && allocateMemory ) { - if ( uv2ScaleMap.matrixAutoUpdate === true ) { + // TODO: Uniformly handle mipmap definitions + // Normal textures and compressed cube textures define base level + mips with their mipmap array + // Uncompressed cube textures use their mipmap array only for mips (no base level) - uv2ScaleMap.updateMatrix(); + if ( mipmaps.length > 0 ) levels ++; - } + state.texStorage2D( 34067, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); + } - } + for ( let i = 0; i < 6; i ++ ) { - } + if ( isDataTexture ) { - function refreshUniformsLine( uniforms, material ) { + if ( useTexStorage ) { - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + state.texSubImage2D( 34069 + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); - } + } else { - function refreshUniformsDash( uniforms, material ) { + state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + } - } + for ( let j = 0; j < mipmaps.length; j ++ ) { - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { + const mipmap = mipmaps[ j ]; + const mipmapImage = mipmap.image[ i ].image; - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; + if ( useTexStorage ) { - if ( material.map ) { + state.texSubImage2D( 34069 + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); - uniforms.map.value = material.map; + } else { - } + state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - if ( material.alphaMap ) { + } - uniforms.alphaMap.value = material.alphaMap; + } - } + } else { - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + if ( useTexStorage ) { - let uvScaleMap; + state.texSubImage2D( 34069 + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); - if ( material.map ) { + } else { - uvScaleMap = material.map; + state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - } else if ( material.alphaMap ) { + } - uvScaleMap = material.alphaMap; + for ( let j = 0; j < mipmaps.length; j ++ ) { - } + const mipmap = mipmaps[ j ]; - if ( uvScaleMap !== undefined ) { + if ( useTexStorage ) { - if ( uvScaleMap.matrixAutoUpdate === true ) { + state.texSubImage2D( 34069 + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); - uvScaleMap.updateMatrix(); + } else { - } + state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } - } + } - } + } - function refreshUniformsSprites( uniforms, material ) { + } - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; + } - if ( material.map ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - uniforms.map.value = material.map; + // We assume images for cube map have the same size. + generateMipmap( 34067 ); - } + } - if ( material.alphaMap ) { + source.__currentVersion = source.version; - uniforms.alphaMap.value = material.alphaMap; + if ( texture.onUpdate ) texture.onUpdate( texture ); } - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + textureProperties.__version = texture.version; - let uvScaleMap; + } - if ( material.map ) { + // Render targets - uvScaleMap = material.map; + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { - } else if ( material.alphaMap ) { + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const renderTargetProperties = properties.get( renderTarget ); - uvScaleMap = material.alphaMap; + if ( ! renderTargetProperties.__hasExternalTextures ) { - } + if ( textureTarget === 32879 || textureTarget === 35866 ) { - if ( uvScaleMap !== undefined ) { + state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); - if ( uvScaleMap.matrixAutoUpdate === true ) { + } else { - uvScaleMap.updateMatrix(); + state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); } - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - } - } + state.bindFramebuffer( 36160, framebuffer ); - function refreshUniformsLambert( uniforms, material ) { + if ( useMultisampledRTT( renderTarget ) ) { - if ( material.emissiveMap ) { + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); - uniforms.emissiveMap.value = material.emissiveMap; + } else { + + _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); } + state.bindFramebuffer( 36160, null ); + } - function refreshUniformsPhong( uniforms, material ) { - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - if ( material.emissiveMap ) { + _gl.bindRenderbuffer( 36161, renderbuffer ); - uniforms.emissiveMap.value = material.emissiveMap; + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - } + let glInternalFormat = 33189; - if ( material.bumpMap ) { + if ( isMultisample || useMultisampledRTT( renderTarget ) ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const depthTexture = renderTarget.depthTexture; - } + if ( depthTexture && depthTexture.isDepthTexture ) { - if ( material.normalMap ) { + if ( depthTexture.type === FloatType ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + glInternalFormat = 36012; - } + } else if ( depthTexture.type === UnsignedIntType ) { - if ( material.displacementMap ) { + glInternalFormat = 33190; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } - } + } - } + const samples = getRenderTargetSamples( renderTarget ); - function refreshUniformsToon( uniforms, material ) { + if ( useMultisampledRTT( renderTarget ) ) { - if ( material.gradientMap ) { + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - uniforms.gradientMap.value = material.gradientMap; + } else { - } + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.emissiveMap ) { + } - uniforms.emissiveMap.value = material.emissiveMap; + } else { - } + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.bumpMap ) { + } - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - } + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - if ( material.normalMap ) { + const samples = getRenderTargetSamples( renderTarget ); - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - } + _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - if ( material.displacementMap ) { + } else if ( useMultisampledRTT( renderTarget ) ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - } + } else { - } + _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - function refreshUniformsStandard( uniforms, material ) { + } - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - if ( material.roughnessMap ) { + _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - uniforms.roughnessMap.value = material.roughnessMap; + } else { - } + // Use the first texture for MRT so far + const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[ 0 ] : renderTarget.texture; - if ( material.metalnessMap ) { + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const samples = getRenderTargetSamples( renderTarget ); - uniforms.metalnessMap.value = material.metalnessMap; + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - } + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.emissiveMap ) { + } else if ( useMultisampledRTT( renderTarget ) ) { - uniforms.emissiveMap.value = material.emissiveMap; + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - } + } else { - if ( material.bumpMap ) { + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } } - if ( material.normalMap ) { - - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - - } + _gl.bindRenderbuffer( 36161, null ); - if ( material.displacementMap ) { + } - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + // Setup resources for a Depth Texture for a FBO (needs an extension) + function setupDepthTexture( framebuffer, renderTarget ) { - } + const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); + if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - const envMap = properties.get( material ).envMap; + state.bindFramebuffer( 36160, framebuffer ); - if ( envMap ) { + if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; + throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); } - } - - function refreshUniformsPhysical( uniforms, material ) { + // upload an empty depth texture with framebuffer size + if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || + renderTarget.depthTexture.image.width !== renderTarget.width || + renderTarget.depthTexture.image.height !== renderTarget.height ) { - refreshUniformsStandard( uniforms, material ); + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + } - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); + setTexture2D( renderTarget.depthTexture, 0 ); - if ( material.clearcoatMap ) { + const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + const samples = getRenderTargetSamples( renderTarget ); - uniforms.clearcoatMap.value = material.clearcoatMap; + if ( renderTarget.depthTexture.format === DepthFormat ) { - } + if ( useMultisampledRTT( renderTarget ) ) { - if ( material.clearcoatRoughnessMap ) { + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 36096, 3553, webglDepthTexture, 0, samples ); - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; + } else { - } + _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - if ( material.clearcoatNormalMap ) { + } - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - if ( material.side === BackSide ) { + if ( useMultisampledRTT( renderTarget ) ) { - uniforms.clearcoatNormalScale.value.negate(); + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 33306, 3553, webglDepthTexture, 0, samples ); - } + } else { - } + _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - uniforms.transmission.value = material.transmission; + } - if ( material.transmissionMap ) { + } else { - uniforms.transmissionMap.value = material.transmissionMap; + throw new Error( 'Unknown depthTexture format' ); } } - function refreshUniformsMatcap( uniforms, material ) { + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { - if ( material.matcap ) { + const renderTargetProperties = properties.get( renderTarget ); + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - uniforms.matcap.value = material.matcap; + if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { - } + if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - if ( material.bumpMap ) { + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } else { - } + if ( isCube ) { - if ( material.normalMap ) { + renderTargetProperties.__webglDepthbuffer = []; - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + for ( let i = 0; i < 6; i ++ ) { - } + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - if ( material.displacementMap ) { + } - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } else { + + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); + + } } - } + state.bindFramebuffer( 36160, null ); - function refreshUniformsDepth( uniforms, material ) { + } - if ( material.displacementMap ) { + // rebind framebuffer with external textures + function rebindTextures( renderTarget, colorTexture, depthTexture ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + const renderTargetProperties = properties.get( renderTarget ); - } + if ( colorTexture !== undefined ) { - } + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, 36064, 3553 ); - function refreshUniformsDistance( uniforms, material ) { + } - if ( material.displacementMap ) { + if ( depthTexture !== undefined ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + setupDepthRenderbuffer( renderTarget ); } - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; - } - function refreshUniformsNormal( uniforms, material ) { + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { - if ( material.bumpMap ) { + const texture = renderTarget.texture; - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - } + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - if ( material.normalMap ) { + if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + if ( textureProperties.__webglTexture === undefined ) { - } + textureProperties.__webglTexture = _gl.createTexture(); - if ( material.displacementMap ) { + } - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + textureProperties.__version = texture.version; + info.memory.textures ++; } - } + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; + // Setup framebuffer - } + if ( isCube ) { - function createCanvasElement() { + renderTargetProperties.__webglFramebuffer = []; - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.style.display = 'block'; - return canvas; + for ( let i = 0; i < 6; i ++ ) { - } + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - function WebGLRenderer( parameters ) { + } - parameters = parameters || {}; + } else { - const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), - _context = parameters.context !== undefined ? parameters.context : null, + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; + if ( isMultipleRenderTargets ) { - let currentRenderList = null; - let currentRenderState = null; + if ( capabilities.drawBuffers ) { - // render() can be called from within a callback triggered by another render. - // We track this so that the nested render call gets its list and state isolated from the parent render call. + const textures = renderTarget.texture; - const renderListStack = []; - const renderStateStack = []; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - // public properties + const attachmentProperties = properties.get( textures[ i ] ); - this.domElement = _canvas; + if ( attachmentProperties.__webglTexture === undefined ) { - // Debug configuration container - this.debug = { + attachmentProperties.__webglTexture = _gl.createTexture(); - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; + info.memory.textures ++; - // clearing + } - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + } - // scene graph + } else { - this.sortObjects = true; + console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' ); - // user-defined clipping + } - this.clippingPlanes = []; - this.localClippingEnabled = false; + } else if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - // physically based shading + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; + _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - // physical lights + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - this.physicallyCorrectLights = false; + state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); + _gl.bindRenderbuffer( 36161, null ); - // tone mapping + if ( renderTarget.depthBuffer ) { - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - // internal properties + } - const _this = this; + state.bindFramebuffer( 36160, null ); - let _isContextLost = false; + } - // internal state cache + } - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentMaterialId = - 1; - - let _currentCamera = null; - - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; + // Setup color buffer - // + if ( isCube ) { - let _width = _canvas.width; - let _height = _canvas.height; + state.bindTexture( 34067, textureProperties.__webglTexture ); + setTextureParameters( 34067, texture, supportsMips ); - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; + for ( let i = 0; i < 6; i ++ ) { - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, 36064, 34069 + i ); - // frustum + } - const _frustum = new Frustum(); + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - // clipping + generateMipmap( 34067 ); - let _clippingEnabled = false; - let _localClippingEnabled = false; + } - // camera matrices cache + state.unbindTexture(); - const _projScreenMatrix = new Matrix4(); + } else if ( isMultipleRenderTargets ) { - const _vector3 = new Vector3(); + const textures = renderTarget.texture; - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - function getTargetPixelRatio() { + const attachment = textures[ i ]; + const attachmentProperties = properties.get( attachment ); - return _currentRenderTarget === null ? _pixelRatio : 1; + state.bindTexture( 3553, attachmentProperties.__webglTexture ); + setTextureParameters( 3553, attachment, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, 36064 + i, 3553 ); - } + if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { - // initialize + generateMipmap( 3553 ); - let _gl = _context; + } - function getContext( contextNames, contextAttributes ) { + } - for ( let i = 0; i < contextNames.length; i ++ ) { + state.unbindTexture(); - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; + } else { - } + let glTextureType = 3553; - return null; + if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { - } + if ( isWebGL2 ) { - try { + glTextureType = renderTarget.isWebGL3DRenderTarget ? 32879 : 35866; - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; + } else { - // event listeners must be registered before WebGL context is created, see #12753 + console.error( 'THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.' ); - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + } - if ( _gl === null ) { + } - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; + state.bindTexture( glTextureType, textureProperties.__webglTexture ); + setTextureParameters( glTextureType, texture, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, 36064, glTextureType ); - if ( _this.isWebGL1Renderer === true ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - contextNames.shift(); + generateMipmap( glTextureType ); } - _gl = getContext( contextNames, contextAttributes ); + state.unbindTexture(); - if ( _gl === null ) { + } - if ( getContext( contextNames ) ) { + // Setup depth and stencil buffers - throw new Error( 'Error creating WebGL context with your selected attributes.' ); + if ( renderTarget.depthBuffer ) { - } else { + setupDepthRenderbuffer( renderTarget ); - throw new Error( 'Error creating WebGL context.' ); + } - } + } - } + function updateRenderTargetMipmap( renderTarget ) { - } + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - // Some experimental-webgl implementations do not have getShaderPrecisionFormat + const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - if ( _gl.getShaderPrecisionFormat === undefined ) { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - _gl.getShaderPrecisionFormat = function () { + const texture = textures[ i ]; - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - }; + const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; + const webglTexture = properties.get( texture ).__webglTexture; - } + state.bindTexture( target, webglTexture ); + generateMipmap( target ); + state.unbindTexture(); - } catch ( error ) { + } - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; + } } - let extensions, capabilities, state, info; - let properties, textures, cubemaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping, shadowMap; - - let background, morphtargets, bufferRenderer, indexedBufferRenderer; + function updateMultisampleRenderTarget( renderTarget ) { - let utils, bindingStates; + if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - function initGLContext() { + const width = renderTarget.width; + const height = renderTarget.height; + let mask = 16384; + const invalidationArray = [ 36064 ]; + const depthStyle = renderTarget.stencilBuffer ? 33306 : 36096; - extensions = new WebGLExtensions( _gl ); + if ( renderTarget.depthBuffer ) { - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); + invalidationArray.push( depthStyle ); - extensions.init( capabilities ); + } - utils = new WebGLUtils( _gl, extensions, capabilities ); + const renderTargetProperties = properties.get( renderTarget ); + const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; - state = new WebGLState( _gl, extensions, capabilities ); + if ( ignoreDepthValues === false ) { - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates( extensions, capabilities ); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); - shadowMap = new WebGLShadowMap( _this, objects, capabilities ); + if ( renderTarget.depthBuffer ) mask |= 256; + if ( renderTarget.stencilBuffer ) mask |= 1024; - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); + } - info.programs = programCache.programs; + state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.shadowMap = shadowMap; - _this.state = state; - _this.info = info; + if ( ignoreDepthValues === true ) { - } + _gl.invalidateFramebuffer( 36008, [ depthStyle ] ); + _gl.invalidateFramebuffer( 36009, [ depthStyle ] ); - initGLContext(); + } - // xr + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - const xr = new WebXRManager( _this, _gl ); + if ( supportsInvalidateFramebuffer ) { - this.xr = xr; + _gl.invalidateFramebuffer( 36008, invalidationArray ); - // API + } - this.getContext = function () { + state.bindFramebuffer( 36008, null ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); - return _gl; + } - }; + } - this.getContextAttributes = function () { + function getRenderTargetSamples( renderTarget ) { - return _gl.getContextAttributes(); + return Math.min( maxSamples, renderTarget.samples ); - }; + } - this.forceContextLoss = function () { + function useMultisampledRTT( renderTarget ) { - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); + const renderTargetProperties = properties.get( renderTarget ); - }; + return isWebGL2 && renderTarget.samples > 0 && extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true && renderTargetProperties.__useRenderToTexture !== false; - this.forceContextRestore = function () { + } - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); + function updateVideoTexture( texture ) { - }; + const frame = info.render.frame; - this.getPixelRatio = function () { + // Check the last frame we updated the VideoTexture - return _pixelRatio; + if ( _videoTextures.get( texture ) !== frame ) { - }; + _videoTextures.set( texture, frame ); + texture.update(); - this.setPixelRatio = function ( value ) { + } - if ( value === undefined ) return; + } - _pixelRatio = value; + function verifyColorSpace( texture, image ) { - this.setSize( _width, _height, false ); + const encoding = texture.encoding; + const format = texture.format; + const type = texture.type; - }; + if ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image; - this.getSize = function ( target ) { + if ( encoding !== LinearEncoding ) { - if ( target === undefined ) { + // sRGB - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); + if ( encoding === sRGBEncoding ) { - target = new Vector2(); + if ( isWebGL2 === false ) { - } + // in WebGL 1, try to use EXT_sRGB extension and unsized formats - return target.set( _width, _height ); + if ( extensions.has( 'EXT_sRGB' ) === true && format === RGBAFormat ) { - }; + texture.format = _SRGBAFormat; - this.setSize = function ( width, height, updateStyle ) { + // it's not possible to generate mips in WebGL 1 with this extension - if ( xr.isPresenting ) { + texture.minFilter = LinearFilter; + texture.generateMipmaps = false; - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; + } else { - } + // slow fallback (CPU decode) - _width = width; - _height = height; + image = ImageUtils.sRGBToLinear( image ); - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); + } - if ( updateStyle !== false ) { + } else { - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format - } + if ( format !== RGBAFormat || type !== UnsignedByteType ) { - this.setViewport( 0, 0, width, height ); + console.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); - }; + } - this.getDrawingBufferSize = function ( target ) { + } - if ( target === undefined ) { + } else { - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); + console.error( 'THREE.WebGLTextures: Unsupported texture encoding:', encoding ); - target = new Vector2(); + } } - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - - }; + return image; - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { + } - _width = width; - _height = height; + // - _pixelRatio = pixelRatio; + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; + this.setTextureCube = setTextureCube; + this.rebindTextures = rebindTextures; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + this.setupDepthRenderbuffer = setupDepthRenderbuffer; + this.setupFrameBufferTexture = setupFrameBufferTexture; + this.useMultisampledRTT = useMultisampledRTT; - this.setViewport( 0, 0, width, height ); + } - }; + function WebGLUtils( gl, extensions, capabilities ) { - this.getCurrentViewport = function ( target ) { + const isWebGL2 = capabilities.isWebGL2; - if ( target === undefined ) { + function convert( p, encoding = null ) { - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); + let extension; - target = new Vector4(); + if ( p === UnsignedByteType ) return 5121; + if ( p === UnsignedShort4444Type ) return 32819; + if ( p === UnsignedShort5551Type ) return 32820; - } + if ( p === ByteType ) return 5120; + if ( p === ShortType ) return 5122; + if ( p === UnsignedShortType ) return 5123; + if ( p === IntType ) return 5124; + if ( p === UnsignedIntType ) return 5125; + if ( p === FloatType ) return 5126; - return target.copy( _currentViewport ); + if ( p === HalfFloatType ) { - }; + if ( isWebGL2 ) return 5131; - this.getViewport = function ( target ) { + extension = extensions.get( 'OES_texture_half_float' ); - return target.copy( _viewport ); + if ( extension !== null ) { - }; + return extension.HALF_FLOAT_OES; - this.setViewport = function ( x, y, width, height ) { + } else { - if ( x.isVector4 ) { + return null; - _viewport.set( x.x, x.y, x.z, x.w ); + } - } else { + } - _viewport.set( x, y, width, height ); + if ( p === AlphaFormat ) return 6406; + if ( p === RGBAFormat ) return 6408; + if ( p === LuminanceFormat ) return 6409; + if ( p === LuminanceAlphaFormat ) return 6410; + if ( p === DepthFormat ) return 6402; + if ( p === DepthStencilFormat ) return 34041; + if ( p === RedFormat ) return 6403; - } + if ( p === RGBFormat ) { - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + console.warn( 'THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228' ); + return 6408; - }; + } - this.getScissor = function ( target ) { + // WebGL 1 sRGB fallback - return target.copy( _scissor ); + if ( p === _SRGBAFormat ) { - }; + extension = extensions.get( 'EXT_sRGB' ); - this.setScissor = function ( x, y, width, height ) { + if ( extension !== null ) { - if ( x.isVector4 ) { + return extension.SRGB_ALPHA_EXT; - _scissor.set( x.x, x.y, x.z, x.w ); + } else { - } else { + return null; - _scissor.set( x, y, width, height ); + } } - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); + // WebGL2 formats. - }; + if ( p === RedIntegerFormat ) return 36244; + if ( p === RGFormat ) return 33319; + if ( p === RGIntegerFormat ) return 33320; + if ( p === RGBAIntegerFormat ) return 36249; - this.getScissorTest = function () { + // S3TC - return _scissorTest; + if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - }; + if ( encoding === sRGBEncoding ) { - this.setScissorTest = function ( boolean ) { + extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' ); - state.setScissorTest( _scissorTest = boolean ); + if ( extension !== null ) { - }; + if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - this.setOpaqueSort = function ( method ) { + } else { - _opaqueSort = method; + return null; - }; + } - this.setTransparentSort = function ( method ) { + } else { - _transparentSort = method; + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - }; + if ( extension !== null ) { - // Clearing + if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - this.getClearColor = function ( target ) { + } else { - if ( target === undefined ) { + return null; - console.warn( 'WebGLRenderer: .getClearColor() now requires a Color as an argument' ); + } - target = new Color(); + } } - return target.copy( background.getClearColor() ); + // PVRTC - }; + if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - this.setClearColor = function () { + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - background.setClearColor.apply( background, arguments ); + if ( extension !== null ) { - }; + if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - this.getClearAlpha = function () { + } else { - return background.getClearAlpha(); + return null; - }; + } - this.setClearAlpha = function () { + } - background.setClearAlpha.apply( background, arguments ); + // ETC1 - }; + if ( p === RGB_ETC1_Format ) { - this.clear = function ( color, depth, stencil ) { + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - let bits = 0; + if ( extension !== null ) { - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; + return extension.COMPRESSED_RGB_ETC1_WEBGL; - _gl.clear( bits ); + } else { - }; + return null; - this.clearColor = function () { + } - this.clear( true, false, false ); + } - }; + // ETC2 - this.clearDepth = function () { + if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - this.clear( false, true, false ); + extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - }; + if ( extension !== null ) { - this.clearStencil = function () { + if ( p === RGB_ETC2_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; + if ( p === RGBA_ETC2_EAC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; - this.clear( false, false, true ); + } else { - }; + return null; - // + } - this.dispose = function () { + } - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + // ASTC - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - objects.dispose(); - bindingStates.dispose(); + if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || + p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || + p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || + p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || + p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { - xr.dispose(); + extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - xr.removeEventListener( 'sessionstart', onXRSessionStart ); - xr.removeEventListener( 'sessionend', onXRSessionEnd ); + if ( extension !== null ) { - animation.stop(); + if ( p === RGBA_ASTC_4x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; + if ( p === RGBA_ASTC_5x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; + if ( p === RGBA_ASTC_5x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; + if ( p === RGBA_ASTC_6x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; + if ( p === RGBA_ASTC_6x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; + if ( p === RGBA_ASTC_8x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; + if ( p === RGBA_ASTC_8x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; + if ( p === RGBA_ASTC_8x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; + if ( p === RGBA_ASTC_10x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; + if ( p === RGBA_ASTC_10x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; + if ( p === RGBA_ASTC_10x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; + if ( p === RGBA_ASTC_10x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; + if ( p === RGBA_ASTC_12x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; + if ( p === RGBA_ASTC_12x12_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; - }; + } else { - // Events + return null; - function onContextLost( event ) { + } - event.preventDefault(); + } - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + // BPTC - _isContextLost = true; + if ( p === RGBA_BPTC_Format ) { - } + extension = extensions.get( 'EXT_texture_compression_bptc' ); - function onContextRestore( /* event */ ) { + if ( extension !== null ) { - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + if ( p === RGBA_BPTC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; - _isContextLost = false; + } else { - const infoAutoReset = info.autoReset; - const shadowMapEnabled = shadowMap.enabled; - const shadowMapAutoUpdate = shadowMap.autoUpdate; - const shadowMapNeedsUpdate = shadowMap.needsUpdate; - const shadowMapType = shadowMap.type; + return null; - initGLContext(); + } - info.autoReset = infoAutoReset; - shadowMap.enabled = shadowMapEnabled; - shadowMap.autoUpdate = shadowMapAutoUpdate; - shadowMap.needsUpdate = shadowMapNeedsUpdate; - shadowMap.type = shadowMapType; + } - } + // - function onMaterialDispose( event ) { + if ( p === UnsignedInt248Type ) { - const material = event.target; + if ( isWebGL2 ) return 34042; - material.removeEventListener( 'dispose', onMaterialDispose ); + extension = extensions.get( 'WEBGL_depth_texture' ); - deallocateMaterial( material ); + if ( extension !== null ) { - } + return extension.UNSIGNED_INT_24_8_WEBGL; - // Buffer deallocation + } else { - function deallocateMaterial( material ) { + return null; - releaseMaterialProgramReferences( material ); + } - properties.remove( material ); + } } + return { convert: convert }; - function releaseMaterialProgramReferences( material ) { - - const programs = properties.get( material ).programs; - - if ( programs !== undefined ) { + } - programs.forEach( function ( program ) { + class ArrayCamera extends PerspectiveCamera { - programCache.releaseProgram( program ); + constructor( array = [] ) { - } ); + super(); - } + this.cameras = array; } - // Buffer rendering + } - function renderObjectImmediate( object, program ) { + ArrayCamera.prototype.isArrayCamera = true; - object.render( function ( object ) { + class Group extends Object3D { - _this.renderBufferImmediate( object, program ); + constructor() { - } ); + super(); - } + this.type = 'Group'; - this.renderBufferImmediate = function ( object, program ) { + } - bindingStates.initAttributes(); + } - const buffers = properties.get( object ); + Group.prototype.isGroup = true; - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + const _moveEvent = { type: 'move' }; - const programAttributes = program.getAttributes(); + class WebXRController { - if ( object.hasPositions ) { + constructor() { - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); + this._targetRay = null; + this._grip = null; + this._hand = null; - bindingStates.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); + } - } + getHandSpace() { - if ( object.hasNormals ) { + if ( this._hand === null ) { - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); + this._hand = new Group(); + this._hand.matrixAutoUpdate = false; + this._hand.visible = false; - bindingStates.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); + this._hand.joints = {}; + this._hand.inputState = { pinching: false }; } - if ( object.hasUvs ) { - - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); - - bindingStates.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); + return this._hand; - } + } - if ( object.hasColors ) { + getTargetRaySpace() { - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); + if ( this._targetRay === null ) { - bindingStates.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); + this._targetRay = new Group(); + this._targetRay.matrixAutoUpdate = false; + this._targetRay.visible = false; + this._targetRay.hasLinearVelocity = false; + this._targetRay.linearVelocity = new Vector3(); + this._targetRay.hasAngularVelocity = false; + this._targetRay.angularVelocity = new Vector3(); } - bindingStates.disableUnusedAttributes(); + return this._targetRay; - _gl.drawArrays( 4, 0, object.count ); + } - object.count = 0; + getGripSpace() { - }; + if ( this._grip === null ) { - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { + this._grip = new Group(); + this._grip.matrixAutoUpdate = false; + this._grip.visible = false; + this._grip.hasLinearVelocity = false; + this._grip.linearVelocity = new Vector3(); + this._grip.hasAngularVelocity = false; + this._grip.angularVelocity = new Vector3(); - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + } - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); + return this._grip; - const program = setProgram( camera, scene, material, object ); + } - state.setMaterial( material, frontFaceCW ); + dispatchEvent( event ) { - // + if ( this._targetRay !== null ) { - let index = geometry.index; - const position = geometry.attributes.position; + this._targetRay.dispatchEvent( event ); - // + } - if ( index === null ) { + if ( this._grip !== null ) { - if ( position === undefined || position.count === 0 ) return; + this._grip.dispatchEvent( event ); - } else if ( index.count === 0 ) { + } - return; + if ( this._hand !== null ) { - } + this._hand.dispatchEvent( event ); - // + } - let rangeFactor = 1; + return this; - if ( material.wireframe === true ) { + } - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; + disconnect( inputSource ) { - } + this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - if ( material.morphTargets || material.morphNormals ) { + if ( this._targetRay !== null ) { - morphtargets.update( object, geometry, material, program ); + this._targetRay.visible = false; } - bindingStates.setup( object, material, program, geometry, index ); + if ( this._grip !== null ) { - let attribute; - let renderer = bufferRenderer; + this._grip.visible = false; - if ( index !== null ) { + } - attribute = attributes.get( index ); + if ( this._hand !== null ) { - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); + this._hand.visible = false; } - // - - const dataCount = ( index !== null ) ? index.count : position.count; - - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; + return this; - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; + } - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + update( inputSource, frame, referenceSpace ) { - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + let inputPose = null; + let gripPose = null; + let handPose = null; - if ( drawCount === 0 ) return; + const targetRay = this._targetRay; + const grip = this._grip; + const hand = this._hand; - // + if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { - if ( object.isMesh ) { + if ( targetRay !== null ) { - if ( material.wireframe === true ) { + inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); + if ( inputPose !== null ) { - } else { + targetRay.matrix.fromArray( inputPose.transform.matrix ); + targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - renderer.setMode( 4 ); + if ( inputPose.linearVelocity ) { - } + targetRay.hasLinearVelocity = true; + targetRay.linearVelocity.copy( inputPose.linearVelocity ); - } else if ( object.isLine ) { + } else { - let lineWidth = material.linewidth; + targetRay.hasLinearVelocity = false; - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + } - state.setLineWidth( lineWidth * getTargetPixelRatio() ); + if ( inputPose.angularVelocity ) { - if ( object.isLineSegments ) { + targetRay.hasAngularVelocity = true; + targetRay.angularVelocity.copy( inputPose.angularVelocity ); - renderer.setMode( 1 ); + } else { - } else if ( object.isLineLoop ) { + targetRay.hasAngularVelocity = false; - renderer.setMode( 2 ); + } - } else { + this.dispatchEvent( _moveEvent ); - renderer.setMode( 3 ); + } } - } else if ( object.isPoints ) { + if ( hand && inputSource.hand ) { - renderer.setMode( 0 ); + handPose = true; - } else if ( object.isSprite ) { + for ( const inputjoint of inputSource.hand.values() ) { - renderer.setMode( 4 ); + // Update the joints groups with the XRJoint poses + const jointPose = frame.getJointPose( inputjoint, referenceSpace ); - } + if ( hand.joints[ inputjoint.jointName ] === undefined ) { - if ( object.isInstancedMesh ) { + // The transform of this joint will be updated with the joint pose on each frame + const joint = new Group(); + joint.matrixAutoUpdate = false; + joint.visible = false; + hand.joints[ inputjoint.jointName ] = joint; + // ?? + hand.add( joint ); - renderer.renderInstances( drawStart, drawCount, object.count ); + } - } else if ( geometry.isInstancedBufferGeometry ) { + const joint = hand.joints[ inputjoint.jointName ]; - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); + if ( jointPose !== null ) { - renderer.renderInstances( drawStart, drawCount, instanceCount ); + joint.matrix.fromArray( jointPose.transform.matrix ); + joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); + joint.jointRadius = jointPose.radius; - } else { + } - renderer.render( drawStart, drawCount ); + joint.visible = jointPose !== null; - } + } - }; + // Custom events - // Compile + // Check pinchz + const indexTip = hand.joints[ 'index-finger-tip' ]; + const thumbTip = hand.joints[ 'thumb-tip' ]; + const distance = indexTip.position.distanceTo( thumbTip.position ); - this.compile = function ( scene, camera ) { + const distanceToPinch = 0.02; + const threshold = 0.005; - currentRenderState = renderStates.get( scene ); - currentRenderState.init(); + if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - scene.traverseVisible( function ( object ) { + hand.inputState.pinching = false; + this.dispatchEvent( { + type: 'pinchend', + handedness: inputSource.handedness, + target: this + } ); - if ( object.isLight && object.layers.test( camera.layers ) ) { + } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - currentRenderState.pushLight( object ); + hand.inputState.pinching = true; + this.dispatchEvent( { + type: 'pinchstart', + handedness: inputSource.handedness, + target: this + } ); - if ( object.castShadow ) { + } - currentRenderState.pushShadow( object ); + } else { - } + if ( grip !== null && inputSource.gripSpace ) { - } + gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - } ); + if ( gripPose !== null ) { - currentRenderState.setupLights(); + grip.matrix.fromArray( gripPose.transform.matrix ); + grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - scene.traverse( function ( object ) { + if ( gripPose.linearVelocity ) { - const material = object.material; + grip.hasLinearVelocity = true; + grip.linearVelocity.copy( gripPose.linearVelocity ); - if ( material ) { + } else { - if ( Array.isArray( material ) ) { + grip.hasLinearVelocity = false; - for ( let i = 0; i < material.length; i ++ ) { + } - const material2 = material[ i ]; + if ( gripPose.angularVelocity ) { - getProgram( material2, scene, object ); + grip.hasAngularVelocity = true; + grip.angularVelocity.copy( gripPose.angularVelocity ); - } + } else { - } else { + grip.hasAngularVelocity = false; - getProgram( material, scene, object ); + } + + } } } - } ); - - }; + } - // Animation Loop + if ( targetRay !== null ) { - let onAnimationFrameCallback = null; + targetRay.visible = ( inputPose !== null ); - function onAnimationFrame( time ) { + } - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + if ( grip !== null ) { - } + grip.visible = ( gripPose !== null ); - function onXRSessionStart() { + } - animation.stop(); + if ( hand !== null ) { - } + hand.visible = ( handPose !== null ); - function onXRSessionEnd() { + } - animation.start(); + return this; } - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); - - if ( typeof window !== 'undefined' ) animation.setContext( window ); + } - this.setAnimationLoop = function ( callback ) { + class DepthTexture extends Texture { - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); + constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - ( callback === null ) ? animation.stop() : animation.start(); + format = format !== undefined ? format : DepthFormat; - }; + if ( format !== DepthFormat && format !== DepthStencilFormat ) { - xr.addEventListener( 'sessionstart', onXRSessionStart ); - xr.addEventListener( 'sessionend', onXRSessionEnd ); + throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - // Rendering + } - this.render = function ( scene, camera ) { + if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; + if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - let renderTarget, forceClear; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( arguments[ 2 ] !== undefined ) { + this.image = { width: width, height: height }; - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; + this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - } + this.flipY = false; + this.generateMipmaps = false; - if ( arguments[ 3 ] !== undefined ) { + } - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; - } + } - if ( camera !== undefined && camera.isCamera !== true ) { + DepthTexture.prototype.isDepthTexture = true; - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + class WebXRManager extends EventDispatcher$1 { - } + constructor( renderer, gl ) { - if ( _isContextLost === true ) return; + super(); - // update scene graph + const scope = this; - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + let session = null; + let framebufferScaleFactor = 1.0; - // update camera matrices and frustum + let referenceSpace = null; + let referenceSpaceType = 'local-floor'; - if ( camera.parent === null ) camera.updateMatrixWorld(); + let pose = null; + let glBinding = null; + let glProjLayer = null; + let glBaseLayer = null; + let xrFrame = null; + const attributes = gl.getContextAttributes(); + let initialRenderTarget = null; + let newRenderTarget = null; - if ( xr.enabled === true && xr.isPresenting === true ) { + const controllers = []; + const inputSourcesMap = new Map(); - camera = xr.getCamera( camera ); + // - } + const cameraL = new PerspectiveCamera(); + cameraL.layers.enable( 1 ); + cameraL.viewport = new Vector4(); - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); + const cameraR = new PerspectiveCamera(); + cameraR.layers.enable( 2 ); + cameraR.viewport = new Vector4(); - currentRenderState = renderStates.get( scene, renderStateStack.length ); - currentRenderState.init(); + const cameras = [ cameraL, cameraR ]; - renderStateStack.push( currentRenderState ); + const cameraVR = new ArrayCamera(); + cameraVR.layers.enable( 1 ); + cameraVR.layers.enable( 2 ); - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); + let _currentDepthNear = null; + let _currentDepthFar = null; - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); + // - currentRenderList = renderLists.get( scene, renderListStack.length ); - currentRenderList.init(); + this.cameraAutoUpdate = true; + this.enabled = false; - renderListStack.push( currentRenderList ); + this.isPresenting = false; - projectObject( scene, camera, 0, _this.sortObjects ); + this.getController = function ( index ) { - currentRenderList.finish(); + let controller = controllers[ index ]; - if ( _this.sortObjects === true ) { + if ( controller === undefined ) { - currentRenderList.sort( _opaqueSort, _transparentSort ); + controller = new WebXRController(); + controllers[ index ] = controller; - } + } - // + return controller.getTargetRaySpace(); - if ( _clippingEnabled === true ) clipping.beginShadows(); + }; - const shadowsArray = currentRenderState.state.shadowsArray; + this.getControllerGrip = function ( index ) { - shadowMap.render( shadowsArray, scene, camera ); + let controller = controllers[ index ]; - currentRenderState.setupLights(); - currentRenderState.setupLightsView( camera ); + if ( controller === undefined ) { - if ( _clippingEnabled === true ) clipping.endShadows(); + controller = new WebXRController(); + controllers[ index ] = controller; - // + } - if ( this.info.autoReset === true ) this.info.reset(); + return controller.getGripSpace(); - if ( renderTarget !== undefined ) { + }; - this.setRenderTarget( renderTarget ); + this.getHand = function ( index ) { - } + let controller = controllers[ index ]; - // + if ( controller === undefined ) { - background.render( currentRenderList, scene, camera, forceClear ); + controller = new WebXRController(); + controllers[ index ] = controller; - // render scene + } - const opaqueObjects = currentRenderList.opaque; - const transparentObjects = currentRenderList.transparent; + return controller.getHandSpace(); - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); + }; // - if ( _currentRenderTarget !== null ) { + function onSessionEvent( event ) { - // Generate mipmap if we're using any kind of mipmap filtering + const controller = inputSourcesMap.get( event.inputSource ); - textures.updateRenderTargetMipmap( _currentRenderTarget ); + if ( controller ) { - // resolve multisample renderbuffers to a single-sample texture if necessary + controller.dispatchEvent( { type: event.type, data: event.inputSource } ); - textures.updateMultisampleRenderTarget( _currentRenderTarget ); + } } - // + function onSessionEnd() { - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); + inputSourcesMap.forEach( function ( controller, inputSource ) { - // Ensure depth buffer writing is enabled so it can be cleared on next render + controller.disconnect( inputSource ); - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); + } ); - state.setPolygonOffset( false ); + inputSourcesMap.clear(); - // _gl.finish(); + _currentDepthNear = null; + _currentDepthFar = null; - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; + // restore framebuffer/rendering state - renderStateStack.pop(); + renderer.setRenderTarget( initialRenderTarget ); - if ( renderStateStack.length > 0 ) { + glBaseLayer = null; + glProjLayer = null; + glBinding = null; + session = null; + newRenderTarget = null; - currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; + // - } else { + animation.stop(); - currentRenderState = null; + scope.isPresenting = false; - } + scope.dispatchEvent( { type: 'sessionend' } ); - renderListStack.pop(); + } - if ( renderListStack.length > 0 ) { + this.setFramebufferScaleFactor = function ( value ) { - currentRenderList = renderListStack[ renderListStack.length - 1 ]; + framebufferScaleFactor = value; - } else { + if ( scope.isPresenting === true ) { - currentRenderList = null; + console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - } + } - }; + }; - function projectObject( object, camera, groupOrder, sortObjects ) { + this.setReferenceSpaceType = function ( value ) { - if ( object.visible === false ) return; + referenceSpaceType = value; - const visible = object.layers.test( camera.layers ); + if ( scope.isPresenting === true ) { - if ( visible ) { + console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - if ( object.isGroup ) { + } - groupOrder = object.renderOrder; + }; - } else if ( object.isLOD ) { + this.getReferenceSpace = function () { - if ( object.autoUpdate === true ) object.update( camera ); + return referenceSpace; - } else if ( object.isLight ) { + }; - currentRenderState.pushLight( object ); + this.getBaseLayer = function () { - if ( object.castShadow ) { + return glProjLayer !== null ? glProjLayer : glBaseLayer; - currentRenderState.pushShadow( object ); + }; - } + this.getBinding = function () { - } else if ( object.isSprite ) { + return glBinding; - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + }; - if ( sortObjects ) { + this.getFrame = function () { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + return xrFrame; - } + }; - const geometry = objects.update( object ); - const material = object.material; + this.getSession = function () { - if ( material.visible ) { + return session; - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + }; - } + this.setSession = async function ( value ) { - } + session = value; - } else if ( object.isImmediateRenderObject ) { + if ( session !== null ) { - if ( sortObjects ) { + initialRenderTarget = renderer.getRenderTarget(); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + session.addEventListener( 'select', onSessionEvent ); + session.addEventListener( 'selectstart', onSessionEvent ); + session.addEventListener( 'selectend', onSessionEvent ); + session.addEventListener( 'squeeze', onSessionEvent ); + session.addEventListener( 'squeezestart', onSessionEvent ); + session.addEventListener( 'squeezeend', onSessionEvent ); + session.addEventListener( 'end', onSessionEnd ); + session.addEventListener( 'inputsourceschange', onInputSourcesChange ); - } + if ( attributes.xrCompatible !== true ) { - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); + await gl.makeXRCompatible(); - } else if ( object.isMesh || object.isLine || object.isPoints ) { + } - if ( object.isSkinnedMesh ) { + if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { - // update skeleton only once in a frame + const layerInit = { + antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, + alpha: attributes.alpha, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; - if ( object.skeleton.frame !== info.render.frame ) { + glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - object.skeleton.update(); - object.skeleton.frame = info.render.frame; + session.updateRenderState( { baseLayer: glBaseLayer } ); - } + newRenderTarget = new WebGLRenderTarget( + glBaseLayer.framebufferWidth, + glBaseLayer.framebufferHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + encoding: renderer.outputEncoding + } + ); - } + } else { - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + let depthFormat = null; + let depthType = null; + let glDepthFormat = null; - if ( sortObjects ) { + if ( attributes.depth ) { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + glDepthFormat = attributes.stencil ? 35056 : 33190; + depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; + depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } - const geometry = objects.update( object ); - const material = object.material; + const projectionlayerInit = { + colorFormat: ( renderer.outputEncoding === sRGBEncoding ) ? 35907 : 32856, + depthFormat: glDepthFormat, + scaleFactor: framebufferScaleFactor + }; - if ( Array.isArray( material ) ) { + glBinding = new XRWebGLBinding( session, gl ); - const groups = geometry.groups; + glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); - for ( let i = 0, l = groups.length; i < l; i ++ ) { + session.updateRenderState( { layers: [ glProjLayer ] } ); - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; + newRenderTarget = new WebGLRenderTarget( + glProjLayer.textureWidth, + glProjLayer.textureHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), + stencilBuffer: attributes.stencil, + encoding: renderer.outputEncoding, + samples: attributes.antialias ? 4 : 0 + } ); - if ( groupMaterial && groupMaterial.visible ) { + const renderTargetProperties = renderer.properties.get( newRenderTarget ); + renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + } - } + newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 - } + // Set foveation to maximum. + this.setFoveation( 1.0 ); - } else if ( material.visible ) { + referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + animation.setContext( session ); + animation.start(); - } + scope.isPresenting = true; - } + scope.dispatchEvent( { type: 'sessionstart' } ); } - } + }; - const children = object.children; + function onInputSourcesChange( event ) { - for ( let i = 0, l = children.length; i < l; i ++ ) { + const inputSources = session.inputSources; - projectObject( children[ i ], camera, groupOrder, sortObjects ); + // Assign inputSources to available controllers - } - - } + for ( let i = 0; i < controllers.length; i ++ ) { - function renderObjects( renderList, scene, camera ) { + inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; + } - for ( let i = 0, l = renderList.length; i < l; i ++ ) { + // Notify disconnected - const renderItem = renderList[ i ]; + for ( let i = 0; i < event.removed.length; i ++ ) { - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; + const inputSource = event.removed[ i ]; + const controller = inputSourcesMap.get( inputSource ); - if ( camera.isArrayCamera ) { + if ( controller ) { - const cameras = camera.cameras; + controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); + inputSourcesMap.delete( inputSource ); - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { + } - const camera2 = cameras[ j ]; + } - if ( object.layers.test( camera2.layers ) ) { + // Notify connected - state.viewport( _currentViewport.copy( camera2.viewport ) ); + for ( let i = 0; i < event.added.length; i ++ ) { - currentRenderState.setupLightsView( camera2 ); + const inputSource = event.added[ i ]; + const controller = inputSourcesMap.get( inputSource ); - renderObject( object, scene, camera2, geometry, material, group ); + if ( controller ) { - } + controller.dispatchEvent( { type: 'connected', data: inputSource } ); } - } else { - - renderObject( object, scene, camera, geometry, material, group ); - } } - } - - function renderObject( object, scene, camera, geometry, material, group ) { + // - object.onBeforeRender( _this, scene, camera, geometry, material, group ); + const cameraLPos = new Vector3(); + const cameraRPos = new Vector3(); - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + /** + * Assumes 2 cameras that are parallel and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 + */ + function setProjectionFromUnion( camera, cameraL, cameraR ) { - if ( object.isImmediateRenderObject ) { + cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); + cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - const program = setProgram( camera, scene, material, object ); + const ipd = cameraLPos.distanceTo( cameraRPos ); - state.setMaterial( material ); + const projL = cameraL.projectionMatrix.elements; + const projR = cameraR.projectionMatrix.elements; - bindingStates.reset(); + // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // Use the left camera for these values. + const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; + const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - renderObjectImmediate( object, program ); + const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + const left = near * leftFov; + const right = near * rightFov; - } else { + // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + const zOffset = ipd / ( - leftFov + rightFov ); + const xOffset = zOffset * - leftFov; - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + // TODO: Better way to apply this offset? + cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); + camera.translateX( xOffset ); + camera.translateZ( zOffset ); + camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - } + // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + const near2 = near + zOffset; + const far2 = far + zOffset; + const left2 = left - xOffset; + const right2 = right + ( ipd - xOffset ); + const top2 = topFov * far / far2 * near2; + const bottom2 = bottomFov * far / far2 * near2; - object.onAfterRender( _this, scene, camera, geometry, material, group ); + camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - } + } - function getProgram( material, scene, object ) { + function updateCamera( camera, parent ) { - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + if ( parent === null ) { - const materialProperties = properties.get( material ); + camera.matrixWorld.copy( camera.matrix ); - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; + } else { - const lightsStateVersion = lights.state.version; + camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); + } - let programs = materialProperties.programs; + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change + } - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment ); + this.updateCamera = function ( camera ) { - if ( programs === undefined ) { + if ( session === null ) return; - // new material + cameraVR.near = cameraR.near = cameraL.near = camera.near; + cameraVR.far = cameraR.far = cameraL.far = camera.far; - material.addEventListener( 'dispose', onMaterialDispose ); + if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - programs = new Map(); - materialProperties.programs = programs; + // Note that the new renderState won't apply until the next frame. See #18320 - } + session.updateRenderState( { + depthNear: cameraVR.near, + depthFar: cameraVR.far + } ); - let program = programs.get( programCacheKey ); + _currentDepthNear = cameraVR.near; + _currentDepthFar = cameraVR.far; - if ( program !== undefined ) { + } - // early out if program and light state is identical + const parent = camera.parent; + const cameras = cameraVR.cameras; - if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { + updateCamera( cameraVR, parent ); - updateCommonMaterialProperties( material, parameters ); + for ( let i = 0; i < cameras.length; i ++ ) { - return program; + updateCamera( cameras[ i ], parent ); } - } else { - - parameters.uniforms = programCache.getUniforms( material ); - - material.onBuild( parameters, _this ); + cameraVR.matrixWorld.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); - material.onBeforeCompile( parameters, _this ); + // update user camera and its children - program = programCache.acquireProgram( parameters, programCacheKey ); - programs.set( programCacheKey, program ); + camera.position.copy( cameraVR.position ); + camera.quaternion.copy( cameraVR.quaternion ); + camera.scale.copy( cameraVR.scale ); + camera.matrix.copy( cameraVR.matrix ); + camera.matrixWorld.copy( cameraVR.matrixWorld ); - materialProperties.uniforms = parameters.uniforms; + const children = camera.children; - } + for ( let i = 0, l = children.length; i < l; i ++ ) { - const uniforms = materialProperties.uniforms; + children[ i ].updateMatrixWorld( true ); - if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { + } - uniforms.clippingPlanes = clipping.uniform; + // update projection matrix for proper view frustum culling - } + if ( cameras.length === 2 ) { - updateCommonMaterialProperties( material, parameters ); + setProjectionFromUnion( cameraVR, cameraL, cameraR ); - // store the light setup it was created for + } else { - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; + // assume single camera setup (AR) - if ( materialProperties.needsLights ) { + cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - // wire up the material to this renderer's lighting state + } - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; + }; - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms + this.getCamera = function () { - } + return cameraVR; - const progUniforms = program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); + }; - materialProperties.currentProgram = program; - materialProperties.uniformsList = uniformsList; + this.getFoveation = function () { - return program; + if ( glProjLayer !== null ) { - } + return glProjLayer.fixedFoveation; - function updateCommonMaterialProperties( material, parameters ) { + } - const materialProperties = properties.get( material ); + if ( glBaseLayer !== null ) { - materialProperties.outputEncoding = parameters.outputEncoding; - materialProperties.instancing = parameters.instancing; - materialProperties.numClippingPlanes = parameters.numClippingPlanes; - materialProperties.numIntersection = parameters.numClipIntersection; - materialProperties.vertexAlphas = parameters.vertexAlphas; + return glBaseLayer.fixedFoveation; - } + } - function setProgram( camera, scene, material, object ) { + return undefined; - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + }; - textures.resetTextureUnits(); + this.setFoveation = function ( foveation ) { - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = cubemaps.get( material.envMap || environment ); - const vertexAlphas = material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4; + // 0 = no foveation = full resolution + // 1 = maximum foveation = the edges render at lower resolution - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; + if ( glProjLayer !== null ) { - if ( _clippingEnabled === true ) { + glProjLayer.fixedFoveation = foveation; - if ( _localClippingEnabled === true || camera !== _currentCamera ) { + } - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; + if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); + glBaseLayer.fixedFoveation = foveation; } - } + }; - // + // Animation Loop - let needsProgramChange = false; + let onAnimationFrameCallback = null; - if ( material.version === materialProperties.__version ) { + function onAnimationFrame( time, frame ) { - if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { + pose = frame.getViewerPose( referenceSpace ); + xrFrame = frame; - needsProgramChange = true; + if ( pose !== null ) { - } else if ( materialProperties.outputEncoding !== encoding ) { + const views = pose.views; - needsProgramChange = true; + if ( glBaseLayer !== null ) { - } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { + renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); + renderer.setRenderTarget( newRenderTarget ); - needsProgramChange = true; + } - } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { + let cameraVRNeedsUpdate = false; - needsProgramChange = true; + // check if it's necessary to rebuild cameraVR's camera list - } else if ( materialProperties.envMap !== envMap ) { + if ( views.length !== cameraVR.cameras.length ) { - needsProgramChange = true; + cameraVR.cameras.length = 0; + cameraVRNeedsUpdate = true; - } else if ( material.fog && materialProperties.fog !== fog ) { + } - needsProgramChange = true; + for ( let i = 0; i < views.length; i ++ ) { - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { + const view = views[ i ]; - needsProgramChange = true; + let viewport = null; - } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { + if ( glBaseLayer !== null ) { - needsProgramChange = true; + viewport = glBaseLayer.getViewport( view ); - } + } else { - } else { + const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); + viewport = glSubImage.viewport; - needsProgramChange = true; - materialProperties.__version = material.version; + // For side-by-side projection, we only produce a single texture for both eyes. + if ( i === 0 ) { - } + renderer.setRenderTargetTextures( + newRenderTarget, + glSubImage.colorTexture, + glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); - // + renderer.setRenderTarget( newRenderTarget ); - let program = materialProperties.currentProgram; + } - if ( needsProgramChange === true ) { + } - program = getProgram( material, scene, object ); + const camera = cameras[ i ]; - } + camera.matrix.fromArray( view.transform.matrix ); + camera.projectionMatrix.fromArray( view.projectionMatrix ); + camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; + if ( i === 0 ) { - const p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; + cameraVR.matrix.copy( camera.matrix ); - if ( state.useProgram( program.program ) ) { + } - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + if ( cameraVRNeedsUpdate === true ) { - } + cameraVR.cameras.push( camera ); - if ( material.id !== _currentMaterialId ) { + } - _currentMaterialId = material.id; + } - refreshMaterial = true; + } - } + // - if ( refreshProgram || _currentCamera !== camera ) { + const inputSources = session.inputSources; - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + for ( let i = 0; i < controllers.length; i ++ ) { - if ( capabilities.logarithmicDepthBuffer ) { + const controller = controllers[ i ]; + const inputSource = inputSources[ i ]; - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + controller.update( inputSource, frame, referenceSpace ); } - if ( _currentCamera !== camera ) { + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - _currentCamera = camera; + xrFrame = null; - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: + } - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done + const animation = new WebGLAnimation(); - } + animation.setAnimationLoop( onAnimationFrame ); - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + this.setAnimationLoop = function ( callback ) { - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { + onAnimationFrameCallback = callback; - const uCamPos = p_uniforms.map.cameraPosition; + }; - if ( uCamPos !== undefined ) { + this.dispose = function () {}; - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); + } - } + } - } + function WebGLMaterials( renderer, properties ) { - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { + function refreshFogUniforms( uniforms, fog ) { - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); + uniforms.fogColor.value.copy( fog.color ); - } + if ( fog.isFog ) { - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - material.skinning ) { + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + } else if ( fog.isFogExp2 ) { - } + uniforms.fogDensity.value = fog.density; } - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures - - if ( material.skinning ) { + } - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); + function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { - const skeleton = object.skeleton; + if ( material.isMeshBasicMaterial ) { - if ( skeleton ) { + refreshUniformsCommon( uniforms, material ); - const bones = skeleton.bones; + } else if ( material.isMeshLambertMaterial ) { - if ( capabilities.floatVertexTextures ) { + refreshUniformsCommon( uniforms, material ); - if ( skeleton.boneTexture === null ) { + } else if ( material.isMeshToonMaterial ) { - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + refreshUniformsCommon( uniforms, material ); + refreshUniformsToon( uniforms, material ); + } else if ( material.isMeshPhongMaterial ) { - let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsPhong( uniforms, material ); - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values + } else if ( material.isMeshStandardMaterial ) { - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsStandard( uniforms, material ); - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; + if ( material.isMeshPhysicalMaterial ) { - } + refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); + } - } else { + } else if ( material.isMeshMatcapMaterial ) { - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsMatcap( uniforms, material ); - } + } else if ( material.isMeshDepthMaterial ) { - } + refreshUniformsCommon( uniforms, material ); - } + } else if ( material.isMeshDistanceMaterial ) { - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { + refreshUniformsCommon( uniforms, material ); + refreshUniformsDistance( uniforms, material ); - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); + } else if ( material.isMeshNormalMaterial ) { - } + refreshUniformsCommon( uniforms, material ); - if ( refreshMaterial ) { + } else if ( material.isLineBasicMaterial ) { - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); + refreshUniformsLine( uniforms, material ); - if ( materialProperties.needsLights ) { + if ( material.isLineDashedMaterial ) { - // the current material requires lighting info + refreshUniformsDash( uniforms, material ); - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required + } - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + } else if ( material.isPointsMaterial ) { - } + refreshUniformsPoints( uniforms, material, pixelRatio, height ); - // refresh uniforms common to several materials + } else if ( material.isSpriteMaterial ) { - if ( fog && material.fog ) { + refreshUniformsSprites( uniforms, material ); - materials.refreshFogUniforms( m_uniforms, fog ); + } else if ( material.isShadowMaterial ) { - } + uniforms.color.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height ); + } else if ( material.isShaderMaterial ) { - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; // #15581 } - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { + } - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; + function refreshUniformsCommon( uniforms, material ) { - } + uniforms.opacity.value = material.opacity; - if ( material.isSpriteMaterial ) { + if ( material.color ) { - p_uniforms.setValue( _gl, 'center', object.center ); + uniforms.diffuse.value.copy( material.color ); } - // common matrices + if ( material.emissive ) { - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); - return program; + } - } + if ( material.map ) { - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + uniforms.map.value = material.map; - function markUniformsLightsNeedsUpdate( uniforms, value ) { + } - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; + if ( material.alphaMap ) { - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; + uniforms.alphaMap.value = material.alphaMap; - } + } - function materialNeedsLights( material ) { + if ( material.bumpMap ) { - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - } + } - this.getActiveCubeFace = function () { + if ( material.displacementMap ) { - return _currentActiveCubeFace; + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - }; + } - this.getActiveMipmapLevel = function () { + if ( material.emissiveMap ) { - return _currentActiveMipmapLevel; + uniforms.emissiveMap.value = material.emissiveMap; - }; + } - this.getRenderTarget = function () { + if ( material.normalMap ) { - return _currentRenderTarget; + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); - }; + } - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { + if ( material.specularMap ) { - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; + uniforms.specularMap.value = material.specularMap; + + } - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + if ( material.alphaTest > 0 ) { - textures.setupRenderTarget( renderTarget ); + uniforms.alphaTest.value = material.alphaTest; } - let framebuffer = null; - let isCube = false; - let isRenderTarget3D = false; + const envMap = properties.get( material ).envMap; - if ( renderTarget ) { + if ( envMap ) { - const texture = renderTarget.texture; + uniforms.envMap.value = envMap; - if ( texture.isDataTexture3D || texture.isDataTexture2DArray ) { + uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - isRenderTarget3D = true; + uniforms.reflectivity.value = material.reflectivity; + uniforms.ior.value = material.ior; + uniforms.refractionRatio.value = material.refractionRatio; - } + } - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; + if ( material.lightMap ) { - if ( renderTarget.isWebGLCubeRenderTarget ) { + uniforms.lightMap.value = material.lightMap; - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; + // artist-friendly light intensity scaling factor + const scaleFactor = ( renderer.physicallyCorrectLights !== true ) ? Math.PI : 1; - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + } - } else { + if ( material.aoMap ) { - framebuffer = __webglFramebuffer; + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - } + } - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. displacementMap map + // 4. normal map + // 5. bump map + // 6. roughnessMap map + // 7. metalnessMap map + // 8. alphaMap map + // 9. emissiveMap map + // 10. clearcoat map + // 11. clearcoat normal map + // 12. clearcoat roughnessMap map + // 13. specular intensity map + // 14. specular tint map + // 15. transmission map + // 16. thickness map - } else { + let uvScaleMap; - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; + if ( material.map ) { - } + uvScaleMap = material.map; - state.bindFramebuffer( 36160, framebuffer ); + } else if ( material.specularMap ) { - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); + uvScaleMap = material.specularMap; - if ( isCube ) { + } else if ( material.displacementMap ) { - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); + uvScaleMap = material.displacementMap; - } else if ( isRenderTarget3D ) { + } else if ( material.normalMap ) { - const textureProperties = properties.get( renderTarget.texture ); - const layer = activeCubeFace || 0; - _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); + uvScaleMap = material.normalMap; - } + } else if ( material.bumpMap ) { - }; + uvScaleMap = material.bumpMap; - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + } else if ( material.roughnessMap ) { - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + uvScaleMap = material.roughnessMap; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + } else if ( material.metalnessMap ) { - } + uvScaleMap = material.metalnessMap; - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + } else if ( material.alphaMap ) { - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + uvScaleMap = material.alphaMap; - framebuffer = framebuffer[ activeCubeFaceIndex ]; + } else if ( material.emissiveMap ) { - } + uvScaleMap = material.emissiveMap; - if ( framebuffer ) { + } else if ( material.clearcoatMap ) { - state.bindFramebuffer( 36160, framebuffer ); + uvScaleMap = material.clearcoatMap; - try { + } else if ( material.clearcoatNormalMap ) { - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; + uvScaleMap = material.clearcoatNormalMap; - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { + } else if ( material.clearcoatRoughnessMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; + uvScaleMap = material.clearcoatRoughnessMap; - } + } else if ( material.specularIntensityMap ) { - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + uvScaleMap = material.specularIntensityMap; - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + } else if ( material.specularColorMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; + uvScaleMap = material.specularColorMap; - } + } else if ( material.transmissionMap ) { - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { + uvScaleMap = material.transmissionMap; - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + } else if ( material.thicknessMap ) { - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + uvScaleMap = material.thicknessMap; - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + } else if ( material.sheenColorMap ) { - } + uvScaleMap = material.sheenColorMap; - } else { + } else if ( material.sheenRoughnessMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + uvScaleMap = material.sheenRoughnessMap; - } + } - } finally { + if ( uvScaleMap !== undefined ) { - // restore framebuffer of current render target if necessary + // backwards compatibility + if ( uvScaleMap.isWebGLRenderTarget ) { - const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; - state.bindFramebuffer( 36160, framebuffer ); + uvScaleMap = uvScaleMap.texture; } - } - - }; + if ( uvScaleMap.matrixAutoUpdate === true ) { - this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { + uvScaleMap.updateMatrix(); - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - const glFormat = utils.convert( texture.format ); + } - textures.setTexture2D( texture, 0 ); + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); + } - state.unbindTexture(); + // uv repeat and offset setting priorities for uv2 + // 1. ao map + // 2. light map - }; + let uv2ScaleMap; - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { + if ( material.aoMap ) { - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); + uv2ScaleMap = material.aoMap; - textures.setTexture2D( dstTexture, 0 ); + } else if ( material.lightMap ) { - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); + uv2ScaleMap = material.lightMap; - if ( srcTexture.isDataTexture ) { + } - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); + if ( uv2ScaleMap !== undefined ) { - } else { + // backwards compatibility + if ( uv2ScaleMap.isWebGLRenderTarget ) { - if ( srcTexture.isCompressedTexture ) { + uv2ScaleMap = uv2ScaleMap.texture; - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); + } - } else { + if ( uv2ScaleMap.matrixAutoUpdate === true ) { - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); + uv2ScaleMap.updateMatrix(); } + uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); + } - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); + } - state.unbindTexture(); + function refreshUniformsLine( uniforms, material ) { - }; + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { + } - if ( _this.isWebGL1Renderer ) { + function refreshUniformsDash( uniforms, material ) { - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); - return; + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; - } + } - const { width, height, data } = srcTexture.image; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - let glTarget; + function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - if ( dstTexture.isDataTexture3D ) { + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * pixelRatio; + uniforms.scale.value = height * 0.5; - textures.setTexture3D( dstTexture, 0 ); - glTarget = 32879; + if ( material.map ) { - } else if ( dstTexture.isDataTexture2DArray ) { + uniforms.map.value = material.map; - textures.setTexture2DArray( dstTexture, 0 ); - glTarget = 35866; + } - } else { + if ( material.alphaMap ) { - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); - return; + uniforms.alphaMap.value = material.alphaMap; } - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); + if ( material.alphaTest > 0 ) { - const unpackRowLen = _gl.getParameter( 3314 ); - const unpackImageHeight = _gl.getParameter( 32878 ); - const unpackSkipPixels = _gl.getParameter( 3316 ); - const unpackSkipRows = _gl.getParameter( 3315 ); - const unpackSkipImages = _gl.getParameter( 32877 ); + uniforms.alphaTest.value = material.alphaTest; - _gl.pixelStorei( 3314, width ); - _gl.pixelStorei( 32878, height ); - _gl.pixelStorei( 3316, sourceBox.min.x ); - _gl.pixelStorei( 3315, sourceBox.min.y ); - _gl.pixelStorei( 32877, sourceBox.min.z ); + } - _gl.texSubImage3D( - glTarget, - level, - position.x, - position.y, - position.z, - sourceBox.max.x - sourceBox.min.x + 1, - sourceBox.max.y - sourceBox.min.y + 1, - sourceBox.max.z - sourceBox.min.z + 1, - glFormat, - glType, - data - ); + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map - _gl.pixelStorei( 3314, unpackRowLen ); - _gl.pixelStorei( 32878, unpackImageHeight ); - _gl.pixelStorei( 3316, unpackSkipPixels ); - _gl.pixelStorei( 3315, unpackSkipRows ); - _gl.pixelStorei( 32877, unpackSkipImages ); + let uvScaleMap; - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); + if ( material.map ) { - state.unbindTexture(); + uvScaleMap = material.map; - }; + } else if ( material.alphaMap ) { - this.initTexture = function ( texture ) { + uvScaleMap = material.alphaMap; - textures.setTexture2D( texture, 0 ); + } - state.unbindTexture(); + if ( uvScaleMap !== undefined ) { - }; + if ( uvScaleMap.matrixAutoUpdate === true ) { - this.resetState = function () { + uvScaleMap.updateMatrix(); - _currentActiveCubeFace = 0; - _currentActiveMipmapLevel = 0; - _currentRenderTarget = null; + } - state.reset(); - bindingStates.reset(); + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - }; + } - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + } - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + function refreshUniformsSprites( uniforms, material ) { - } + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; - } + if ( material.map ) { - class WebGL1Renderer extends WebGLRenderer {} + uniforms.map.value = material.map; - WebGL1Renderer.prototype.isWebGL1Renderer = true; + } - class Scene extends Object3D { + if ( material.alphaMap ) { - constructor() { + uniforms.alphaMap.value = material.alphaMap; - super(); + } - this.type = 'Scene'; + if ( material.alphaTest > 0 ) { - this.background = null; - this.environment = null; - this.fog = null; + uniforms.alphaTest.value = material.alphaTest; - this.overrideMaterial = null; + } - this.autoUpdate = true; // checked by the renderer + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + let uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.alphaMap ) { - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + uvScaleMap = material.alphaMap; } - } + if ( uvScaleMap !== undefined ) { - copy( source, recursive ) { + if ( uvScaleMap.matrixAutoUpdate === true ) { - super.copy( source, recursive ); + uvScaleMap.updateMatrix(); - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); + } - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; + } - return this; + } + + function refreshUniformsPhong( uniforms, material ) { + + uniforms.specular.value.copy( material.specular ); + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } - toJSON( meta ) { + function refreshUniformsToon( uniforms, material ) { - const data = super.toJSON( meta ); + if ( material.gradientMap ) { - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); + uniforms.gradientMap.value = material.gradientMap; - return data; + } } - } + function refreshUniformsStandard( uniforms, material ) { - Scene.prototype.isScene = true; + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; - class InterleavedBuffer { + if ( material.roughnessMap ) { - constructor( array, stride ) { + uniforms.roughnessMap.value = material.roughnessMap; - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; + } - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; + if ( material.metalnessMap ) { - this.version = 0; + uniforms.metalnessMap.value = material.metalnessMap; - this.uuid = generateUUID(); + } - this.onUploadCallback = function () {}; + const envMap = properties.get( material ).envMap; - } + if ( envMap ) { - set needsUpdate( value ) { + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; - if ( value === true ) this.version ++; + } } - setUsage( value ) { - - this.usage = value; + function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { - return this; + uniforms.ior.value = material.ior; // also part of uniforms common - } + if ( material.sheen > 0 ) { - copy( source ) { + uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; + uniforms.sheenRoughness.value = material.sheenRoughness; - return this; + if ( material.sheenColorMap ) { - } + uniforms.sheenColorMap.value = material.sheenColorMap; - copyAt( index1, attribute, index2 ) { + } - index1 *= this.stride; - index2 *= attribute.stride; + if ( material.sheenRoughnessMap ) { - for ( let i = 0, l = this.stride; i < l; i ++ ) { + uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + } } - return this; - - } + if ( material.clearcoat > 0 ) { - set( value, offset = 0 ) { + uniforms.clearcoat.value = material.clearcoat; + uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - this.array.set( value, offset ); + if ( material.clearcoatMap ) { - return this; + uniforms.clearcoatMap.value = material.clearcoatMap; - } + } - clone( data ) { + if ( material.clearcoatRoughnessMap ) { - if ( data.arrayBuffers === undefined ) { + uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - data.arrayBuffers = {}; + } - } + if ( material.clearcoatNormalMap ) { - if ( this.array.buffer._uuid === undefined ) { + uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); + uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - this.array.buffer._uuid = generateUUID(); + if ( material.side === BackSide ) { - } + uniforms.clearcoatNormalScale.value.negate(); - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + } - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; + } } - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - - const ib = new InterleavedBuffer( array, this.stride ); - ib.setUsage( this.usage ); + if ( material.transmission > 0 ) { - return ib; + uniforms.transmission.value = material.transmission; + uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; + uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); - } + if ( material.transmissionMap ) { - onUpload( callback ) { + uniforms.transmissionMap.value = material.transmissionMap; - this.onUploadCallback = callback; + } - return this; + uniforms.thickness.value = material.thickness; - } + if ( material.thicknessMap ) { - toJSON( data ) { + uniforms.thicknessMap.value = material.thicknessMap; - if ( data.arrayBuffers === undefined ) { + } - data.arrayBuffers = {}; + uniforms.attenuationDistance.value = material.attenuationDistance; + uniforms.attenuationColor.value.copy( material.attenuationColor ); } - // generate UUID for array buffer if necessary + uniforms.specularIntensity.value = material.specularIntensity; + uniforms.specularColor.value.copy( material.specularColor ); - if ( this.array.buffer._uuid === undefined ) { + if ( material.specularIntensityMap ) { - this.array.buffer._uuid = generateUUID(); + uniforms.specularIntensityMap.value = material.specularIntensityMap; } - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + if ( material.specularColorMap ) { - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); + uniforms.specularColorMap.value = material.specularColorMap; } - // - - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; - } - } - - InterleavedBuffer.prototype.isInterleavedBuffer = true; - - const _vector$6 = new Vector3(); - - class InterleavedBufferAttribute { - - constructor( interleavedBuffer, itemSize, offset, normalized ) { + function refreshUniformsMatcap( uniforms, material ) { - this.name = ''; + if ( material.matcap ) { - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; + uniforms.matcap.value = material.matcap; - this.normalized = normalized === true; + } } - get count() { + function refreshUniformsDistance( uniforms, material ) { - return this.data.count; + uniforms.referencePosition.value.copy( material.referencePosition ); + uniforms.nearDistance.value = material.nearDistance; + uniforms.farDistance.value = material.farDistance; } - get array() { + return { + refreshFogUniforms: refreshFogUniforms, + refreshMaterialUniforms: refreshMaterialUniforms + }; - return this.data.array; + } - } + function createCanvasElement() { - set needsUpdate( value ) { + const canvas = createElementNS( 'canvas' ); + canvas.style.display = 'block'; + return canvas; - this.data.needsUpdate = value; + } - } + function WebGLRenderer( parameters = {} ) { - applyMatrix4( m ) { + const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), + _context = parameters.context !== undefined ? parameters.context : null, - for ( let i = 0, l = this.data.count; i < l; i ++ ) { + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', + _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + let _alpha; - _vector$6.applyMatrix4( m ); + if ( parameters.context !== undefined ) { - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + _alpha = _context.getContextAttributes().alpha; - } + } else { - return this; + _alpha = parameters.alpha !== undefined ? parameters.alpha : false; } - applyNormalMatrix( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { - - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + let currentRenderList = null; + let currentRenderState = null; - _vector$6.applyNormalMatrix( m ); + // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + const renderListStack = []; + const renderStateStack = []; - } + // public properties - return this; + this.domElement = _canvas; - } + // Debug configuration container + this.debug = { - transformDirection( m ) { + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true + }; - for ( let i = 0, l = this.count; i < l; i ++ ) { + // clearing - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - _vector$6.transformDirection( m ); + // scene graph - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + this.sortObjects = true; - } + // user-defined clipping - return this; + this.clippingPlanes = []; + this.localClippingEnabled = false; - } + // physically based shading - setX( index, x ) { + this.outputEncoding = LinearEncoding; - this.data.array[ index * this.data.stride + this.offset ] = x; + // physical lights - return this; + this.physicallyCorrectLights = false; - } + // tone mapping - setY( index, y ) { + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + // internal properties - return this; + const _this = this; - } + let _isContextLost = false; - setZ( index, z ) { + // internal state cache - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + let _currentActiveCubeFace = 0; + let _currentActiveMipmapLevel = 0; + let _currentRenderTarget = null; + let _currentMaterialId = - 1; - return this; + let _currentCamera = null; - } + const _currentViewport = new Vector4(); + const _currentScissor = new Vector4(); + let _currentScissorTest = null; - setW( index, w ) { + // - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + let _width = _canvas.width; + let _height = _canvas.height; - return this; + let _pixelRatio = 1; + let _opaqueSort = null; + let _transparentSort = null; - } + const _viewport = new Vector4( 0, 0, _width, _height ); + const _scissor = new Vector4( 0, 0, _width, _height ); + let _scissorTest = false; - getX( index ) { + // frustum - return this.data.array[ index * this.data.stride + this.offset ]; + const _frustum = new Frustum(); - } + // clipping - getY( index ) { + let _clippingEnabled = false; + let _localClippingEnabled = false; - return this.data.array[ index * this.data.stride + this.offset + 1 ]; + // transmission - } + let _transmissionRenderTarget = null; - getZ( index ) { + // camera matrices cache - return this.data.array[ index * this.data.stride + this.offset + 2 ]; + const _projScreenMatrix = new Matrix4(); - } + const _vector2 = new Vector2(); + const _vector3 = new Vector3(); - getW( index ) { + const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - return this.data.array[ index * this.data.stride + this.offset + 3 ]; + function getTargetPixelRatio() { + + return _currentRenderTarget === null ? _pixelRatio : 1; } - setXY( index, x, y ) { + // initialize - index = index * this.data.stride + this.offset; + let _gl = _context; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; + function getContext( contextNames, contextAttributes ) { - return this; + for ( let i = 0; i < contextNames.length; i ++ ) { - } + const contextName = contextNames[ i ]; + const context = _canvas.getContext( contextName, contextAttributes ); + if ( context !== null ) return context; - setXYZ( index, x, y, z ) { + } - index = index * this.data.stride + this.offset; + return null; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; + } - return this; + try { - } + const contextAttributes = { + alpha: true, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer, + powerPreference: _powerPreference, + failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat + }; - setXYZW( index, x, y, z, w ) { + // OffscreenCanvas does not have setAttribute, see #22811 + if ( 'setAttribute' in _canvas ) _canvas.setAttribute( 'data-engine', `three.js r${REVISION}` ); - index = index * this.data.stride + this.offset; + // event listeners must be registered before WebGL context is created, see #12753 + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; + if ( _gl === null ) { - return this; + const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - } + if ( _this.isWebGL1Renderer === true ) { - clone( data ) { + contextNames.shift(); - if ( data === undefined ) { + } - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); + _gl = getContext( contextNames, contextAttributes ); - const array = []; + if ( _gl === null ) { - for ( let i = 0; i < this.count; i ++ ) { + if ( getContext( contextNames ) ) { - const index = i * this.data.stride + this.offset; + throw new Error( 'Error creating WebGL context with your selected attributes.' ); - for ( let j = 0; j < this.itemSize; j ++ ) { + } else { - array.push( this.data.array[ index + j ] ); + throw new Error( 'Error creating WebGL context.' ); } } - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - } else { + } - if ( data.interleavedBuffers === undefined ) { + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - data.interleavedBuffers = {}; + if ( _gl.getShaderPrecisionFormat === undefined ) { - } + _gl.getShaderPrecisionFormat = function () { - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); + }; - } + } - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); + } catch ( error ) { - } + console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; } - toJSON( data ) { + let extensions, capabilities, state, info; + let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let programCache, materials, renderLists, renderStates, clipping, shadowMap; - if ( data === undefined ) { + let background, morphtargets, bufferRenderer, indexedBufferRenderer; - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); + let utils, bindingStates; - const array = []; + function initGLContext() { - for ( let i = 0; i < this.count; i ++ ) { + extensions = new WebGLExtensions( _gl ); - const index = i * this.data.stride + this.offset; + capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - for ( let j = 0; j < this.itemSize; j ++ ) { + extensions.init( capabilities ); - array.push( this.data.array[ index + j ] ); + utils = new WebGLUtils( _gl, extensions, capabilities ); - } + state = new WebGLState( _gl, extensions, capabilities ); - } + info = new WebGLInfo( _gl ); + properties = new WebGLProperties(); + textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); + cubemaps = new WebGLCubeMaps( _this ); + cubeuvmaps = new WebGLCubeUVMaps( _this ); + attributes = new WebGLAttributes( _gl, capabilities ); + bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); + geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); + morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); + clipping = new WebGLClipping( properties ); + programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + materials = new WebGLMaterials( _this, properties ); + renderLists = new WebGLRenderLists(); + renderStates = new WebGLRenderStates( extensions, capabilities ); + background = new WebGLBackground( _this, cubemaps, state, objects, _alpha, _premultipliedAlpha ); + shadowMap = new WebGLShadowMap( _this, objects, capabilities ); - // deinterleave data and save it as an ordinary buffer attribute for now + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; + info.programs = programCache.programs; - } else { + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.shadowMap = shadowMap; + _this.state = state; + _this.info = info; - // save as true interlaved attribtue + } - if ( data.interleavedBuffers === undefined ) { + initGLContext(); - data.interleavedBuffers = {}; + // xr - } + const xr = new WebXRManager( _this, _gl ); - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + this.xr = xr; - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); + // API - } + this.getContext = function () { - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; + return _gl; - } + }; - } + this.getContextAttributes = function () { - } + return _gl.getContextAttributes(); - InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; + }; - /** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ + this.forceContextLoss = function () { - class SpriteMaterial extends Material { + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.loseContext(); - constructor( parameters ) { + }; - super(); + this.forceContextRestore = function () { - this.type = 'SpriteMaterial'; + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.restoreContext(); - this.color = new Color( 0xffffff ); + }; - this.map = null; + this.getPixelRatio = function () { - this.alphaMap = null; + return _pixelRatio; - this.rotation = 0; + }; - this.sizeAttenuation = true; + this.setPixelRatio = function ( value ) { - this.transparent = true; + if ( value === undefined ) return; - this.setValues( parameters ); + _pixelRatio = value; - } + this.setSize( _width, _height, false ); - copy( source ) { + }; - super.copy( source ); + this.getSize = function ( target ) { - this.color.copy( source.color ); + return target.set( _width, _height ); - this.map = source.map; + }; - this.alphaMap = source.alphaMap; + this.setSize = function ( width, height, updateStyle ) { - this.rotation = source.rotation; + if ( xr.isPresenting ) { - this.sizeAttenuation = source.sizeAttenuation; + console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + return; - return this; + } - } + _width = width; + _height = height; - } + _canvas.width = Math.floor( width * _pixelRatio ); + _canvas.height = Math.floor( height * _pixelRatio ); - SpriteMaterial.prototype.isSpriteMaterial = true; + if ( updateStyle !== false ) { - let _geometry; + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; - const _intersectPoint = /*@__PURE__*/ new Vector3(); - const _worldScale = /*@__PURE__*/ new Vector3(); - const _mvPosition = /*@__PURE__*/ new Vector3(); + } - const _alignedPosition = /*@__PURE__*/ new Vector2(); - const _rotatedPosition = /*@__PURE__*/ new Vector2(); - const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); + this.setViewport( 0, 0, width, height ); - const _vA = /*@__PURE__*/ new Vector3(); - const _vB = /*@__PURE__*/ new Vector3(); - const _vC = /*@__PURE__*/ new Vector3(); + }; - const _uvA = /*@__PURE__*/ new Vector2(); - const _uvB = /*@__PURE__*/ new Vector2(); - const _uvC = /*@__PURE__*/ new Vector2(); + this.getDrawingBufferSize = function ( target ) { - class Sprite extends Object3D { + return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - constructor( material ) { + }; - super(); + this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - this.type = 'Sprite'; + _width = width; + _height = height; - if ( _geometry === undefined ) { + _pixelRatio = pixelRatio; - _geometry = new BufferGeometry(); + _canvas.width = Math.floor( width * pixelRatio ); + _canvas.height = Math.floor( height * pixelRatio ); - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); + this.setViewport( 0, 0, width, height ); - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); + }; - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); + this.getCurrentViewport = function ( target ) { - } + return target.copy( _currentViewport ); - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); + }; - this.center = new Vector2( 0.5, 0.5 ); + this.getViewport = function ( target ) { - } + return target.copy( _viewport ); - raycast( raycaster, intersects ) { + }; - if ( raycaster.camera === null ) { + this.setViewport = function ( x, y, width, height ) { - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); + if ( x.isVector4 ) { - } + _viewport.set( x.x, x.y, x.z, x.w ); - _worldScale.setFromMatrixScale( this.matrixWorld ); + } else { - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); + _viewport.set( x, y, width, height ); - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + } - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - _worldScale.multiplyScalar( - _mvPosition.z ); + }; - } + this.getScissor = function ( target ) { - const rotation = this.material.rotation; - let sin, cos; + return target.copy( _scissor ); - if ( rotation !== 0 ) { + }; - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); + this.setScissor = function ( x, y, width, height ) { - } + if ( x.isVector4 ) { - const center = this.center; + _scissor.set( x.x, x.y, x.z, x.w ); - transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + } else { - _uvA.set( 0, 0 ); - _uvB.set( 1, 0 ); - _uvC.set( 1, 1 ); + _scissor.set( x, y, width, height ); - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); + } - if ( intersect === null ) { + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - // check second triangle - transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB.set( 0, 1 ); + }; - intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); - if ( intersect === null ) { + this.getScissorTest = function () { - return; + return _scissorTest; - } + }; - } + this.setScissorTest = function ( boolean ) { - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); + state.setScissorTest( _scissorTest = boolean ); - if ( distance < raycaster.near || distance > raycaster.far ) return; + }; - intersects.push( { + this.setOpaqueSort = function ( method ) { - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), - face: null, - object: this + _opaqueSort = method; - } ); + }; - } + this.setTransparentSort = function ( method ) { - copy( source ) { + _transparentSort = method; - super.copy( source ); + }; - if ( source.center !== undefined ) this.center.copy( source.center ); + // Clearing - this.material = source.material; + this.getClearColor = function ( target ) { - return this; + return target.copy( background.getClearColor() ); - } + }; - } + this.setClearColor = function () { - Sprite.prototype.isSprite = true; + background.setClearColor.apply( background, arguments ); - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { + }; - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); + this.getClearAlpha = function () { - // to check if rotation is not zero - if ( sin !== undefined ) { + return background.getClearAlpha(); - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); + }; - } else { + this.setClearAlpha = function () { - _rotatedPosition.copy( _alignedPosition ); + background.setClearAlpha.apply( background, arguments ); - } + }; + this.clear = function ( color = true, depth = true, stencil = true ) { - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; + let bits = 0; - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); + if ( color ) bits |= 16384; + if ( depth ) bits |= 256; + if ( stencil ) bits |= 1024; - } + _gl.clear( bits ); - const _basePosition = /*@__PURE__*/ new Vector3(); + }; - const _skinIndex = /*@__PURE__*/ new Vector4(); - const _skinWeight = /*@__PURE__*/ new Vector4(); + this.clearColor = function () { - const _vector$5 = /*@__PURE__*/ new Vector3(); - const _matrix = /*@__PURE__*/ new Matrix4(); + this.clear( true, false, false ); - class SkinnedMesh extends Mesh { + }; - constructor( geometry, material ) { + this.clearDepth = function () { - super( geometry, material ); + this.clear( false, true, false ); - this.type = 'SkinnedMesh'; + }; - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); + this.clearStencil = function () { - } + this.clear( false, false, true ); - copy( source ) { + }; - super.copy( source ); + // - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); + this.dispose = function () { - this.skeleton = source.skeleton; + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - return this; + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + cubeuvmaps.dispose(); + objects.dispose(); + bindingStates.dispose(); + programCache.dispose(); - } + xr.dispose(); - bind( skeleton, bindMatrix ) { + xr.removeEventListener( 'sessionstart', onXRSessionStart ); + xr.removeEventListener( 'sessionend', onXRSessionEnd ); - this.skeleton = skeleton; + if ( _transmissionRenderTarget ) { - if ( bindMatrix === undefined ) { + _transmissionRenderTarget.dispose(); + _transmissionRenderTarget = null; - this.updateMatrixWorld( true ); + } - this.skeleton.calculateInverses(); + animation.stop(); - bindMatrix = this.matrixWorld; + }; - } + // Events - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.copy( bindMatrix ).invert(); + function onContextLost( event ) { - } + event.preventDefault(); - pose() { + console.log( 'THREE.WebGLRenderer: Context Lost.' ); - this.skeleton.pose(); + _isContextLost = true; } - normalizeSkinWeights() { + function onContextRestore( /* event */ ) { - const vector = new Vector4(); + console.log( 'THREE.WebGLRenderer: Context Restored.' ); - const skinWeight = this.geometry.attributes.skinWeight; + _isContextLost = false; - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { + const infoAutoReset = info.autoReset; + const shadowMapEnabled = shadowMap.enabled; + const shadowMapAutoUpdate = shadowMap.autoUpdate; + const shadowMapNeedsUpdate = shadowMap.needsUpdate; + const shadowMapType = shadowMap.type; - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); + initGLContext(); - const scale = 1.0 / vector.manhattanLength(); + info.autoReset = infoAutoReset; + shadowMap.enabled = shadowMapEnabled; + shadowMap.autoUpdate = shadowMapAutoUpdate; + shadowMap.needsUpdate = shadowMapNeedsUpdate; + shadowMap.type = shadowMapType; - if ( scale !== Infinity ) { + } - vector.multiplyScalar( scale ); + function onMaterialDispose( event ) { - } else { + const material = event.target; - vector.set( 1, 0, 0, 0 ); // do something reasonable + material.removeEventListener( 'dispose', onMaterialDispose ); - } + deallocateMaterial( material ); - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); + } - } + // Buffer deallocation + + function deallocateMaterial( material ) { + + releaseMaterialProgramReferences( material ); + + properties.remove( material ); } - updateMatrixWorld( force ) { - super.updateMatrixWorld( force ); + function releaseMaterialProgramReferences( material ) { - if ( this.bindMode === 'attached' ) { + const programs = properties.get( material ).programs; - this.bindMatrixInverse.copy( this.matrixWorld ).invert(); + if ( programs !== undefined ) { - } else if ( this.bindMode === 'detached' ) { + programs.forEach( function ( program ) { - this.bindMatrixInverse.copy( this.bindMatrix ).invert(); + programCache.releaseProgram( program ); - } else { + } ); - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); + if ( material.isShaderMaterial ) { + + programCache.releaseShaderCache( material ); + + } } } - boneTransform( index, target ) { + // Buffer rendering - const skeleton = this.skeleton; - const geometry = this.geometry; + this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); + if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + + const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - _basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); + const program = setProgram( camera, scene, geometry, material, object ); - target.set( 0, 0, 0 ); + state.setMaterial( material, frontFaceCW ); - for ( let i = 0; i < 4; i ++ ) { + // - const weight = _skinWeight.getComponent( i ); + let index = geometry.index; + const position = geometry.attributes.position; - if ( weight !== 0 ) { + // - const boneIndex = _skinIndex.getComponent( i ); + if ( index === null ) { - _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); + if ( position === undefined || position.count === 0 ) return; - target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); + } else if ( index.count === 0 ) { - } + return; } - return target.applyMatrix4( this.bindMatrixInverse ); + // - } + let rangeFactor = 1; - } + if ( material.wireframe === true ) { - SkinnedMesh.prototype.isSkinnedMesh = true; + index = geometries.getWireframeAttribute( geometry ); + rangeFactor = 2; - class Bone extends Object3D { + } - constructor() { + bindingStates.setup( object, material, program, geometry, index ); - super(); + let attribute; + let renderer = bufferRenderer; - this.type = 'Bone'; + if ( index !== null ) { - } + attribute = attributes.get( index ); - } + renderer = indexedBufferRenderer; + renderer.setIndex( attribute ); - Bone.prototype.isBone = true; + } - const _offsetMatrix = /*@__PURE__*/ new Matrix4(); - const _identityMatrix = /*@__PURE__*/ new Matrix4(); + // - class Skeleton { + const dataCount = ( index !== null ) ? index.count : position.count; - constructor( bones = [], boneInverses = [] ) { + const rangeStart = geometry.drawRange.start * rangeFactor; + const rangeCount = geometry.drawRange.count * rangeFactor; - this.uuid = generateUUID(); + const groupStart = group !== null ? group.start * rangeFactor : 0; + const groupCount = group !== null ? group.count * rangeFactor : Infinity; - this.bones = bones.slice( 0 ); - this.boneInverses = boneInverses; - this.boneMatrices = null; + const drawStart = Math.max( rangeStart, groupStart ); + const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - this.boneTexture = null; - this.boneTextureSize = 0; + const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - this.frame = - 1; + if ( drawCount === 0 ) return; - this.init(); + // - } + if ( object.isMesh ) { - init() { + if ( material.wireframe === true ) { - const bones = this.bones; - const boneInverses = this.boneInverses; + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( 1 ); - this.boneMatrices = new Float32Array( bones.length * 16 ); + } else { - // calculate inverse bone matrices if necessary + renderer.setMode( 4 ); - if ( boneInverses.length === 0 ) { + } - this.calculateInverses(); + } else if ( object.isLine ) { - } else { + let lineWidth = material.linewidth; - // handle special case + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - if ( bones.length !== boneInverses.length ) { + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); + if ( object.isLineSegments ) { - this.boneInverses = []; + renderer.setMode( 1 ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } else if ( object.isLineLoop ) { - this.boneInverses.push( new Matrix4() ); + renderer.setMode( 2 ); - } + } else { + + renderer.setMode( 3 ); } - } + } else if ( object.isPoints ) { - } + renderer.setMode( 0 ); - calculateInverses() { + } else if ( object.isSprite ) { - this.boneInverses.length = 0; + renderer.setMode( 4 ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } - const inverse = new Matrix4(); + if ( object.isInstancedMesh ) { - if ( this.bones[ i ] ) { + renderer.renderInstances( drawStart, drawCount, object.count ); - inverse.copy( this.bones[ i ].matrixWorld ).invert(); + } else if ( geometry.isInstancedBufferGeometry ) { - } + const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - this.boneInverses.push( inverse ); + renderer.renderInstances( drawStart, drawCount, instanceCount ); + + } else { + + renderer.render( drawStart, drawCount ); } - } + }; - pose() { + // Compile - // recover the bind-time world matrices + this.compile = function ( scene, camera ) { - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + currentRenderState = renderStates.get( scene ); + currentRenderState.init(); - const bone = this.bones[ i ]; + renderStateStack.push( currentRenderState ); - if ( bone ) { + scene.traverseVisible( function ( object ) { - bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); + if ( object.isLight && object.layers.test( camera.layers ) ) { - } + currentRenderState.pushLight( object ); - } + if ( object.castShadow ) { - // compute the local matrices, positions, rotations and scales + currentRenderState.pushShadow( object ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } - const bone = this.bones[ i ]; + } - if ( bone ) { + } ); - if ( bone.parent && bone.parent.isBone ) { + currentRenderState.setupLights( _this.physicallyCorrectLights ); - bone.matrix.copy( bone.parent.matrixWorld ).invert(); - bone.matrix.multiply( bone.matrixWorld ); + scene.traverse( function ( object ) { - } else { + const material = object.material; - bone.matrix.copy( bone.matrixWorld ); + if ( material ) { - } + if ( Array.isArray( material ) ) { - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + for ( let i = 0; i < material.length; i ++ ) { - } + const material2 = material[ i ]; - } + getProgram( material2, scene, object ); - } + } - update() { + } else { - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; + getProgram( material, scene, object ); - // flatten bone matrices to array + } - for ( let i = 0, il = bones.length; i < il; i ++ ) { + } - // compute the offset between the current and the original transform + } ); - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; + renderStateStack.pop(); + currentRenderState = null; - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); + }; - } + // Animation Loop - if ( boneTexture !== null ) { + let onAnimationFrameCallback = null; - boneTexture.needsUpdate = true; + function onAnimationFrame( time ) { - } + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } - clone() { + function onXRSessionStart() { - return new Skeleton( this.bones, this.boneInverses ); + animation.stop(); } - getBoneByName( name ) { + function onXRSessionEnd() { - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + animation.start(); - const bone = this.bones[ i ]; + } - if ( bone.name === name ) { + const animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - return bone; + if ( typeof self !== 'undefined' ) animation.setContext( self ); - } + this.setAnimationLoop = function ( callback ) { - } + onAnimationFrameCallback = callback; + xr.setAnimationLoop( callback ); - return undefined; + ( callback === null ) ? animation.stop() : animation.start(); - } + }; - dispose( ) { + xr.addEventListener( 'sessionstart', onXRSessionStart ); + xr.addEventListener( 'sessionend', onXRSessionEnd ); - if ( this.boneTexture !== null ) { + // Rendering - this.boneTexture.dispose(); + this.render = function ( scene, camera ) { - this.boneTexture = null; + if ( camera !== undefined && camera.isCamera !== true ) { - } + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - } + } - fromJSON( json, bones ) { + if ( _isContextLost === true ) return; - this.uuid = json.uuid; + // update scene graph - for ( let i = 0, l = json.bones.length; i < l; i ++ ) { + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - const uuid = json.bones[ i ]; - let bone = bones[ uuid ]; + // update camera matrices and frustum - if ( bone === undefined ) { + if ( camera.parent === null ) camera.updateMatrixWorld(); - console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); - bone = new Bone(); + if ( xr.enabled === true && xr.isPresenting === true ) { - } + if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - this.bones.push( bone ); - this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); + camera = xr.getCamera(); // use XR camera for rendering } - this.init(); + // + if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - return this; + currentRenderState = renderStates.get( scene, renderStateStack.length ); + currentRenderState.init(); - } + renderStateStack.push( currentRenderState ); - toJSON() { + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); - const data = { - metadata: { - version: 4.5, - type: 'Skeleton', - generator: 'Skeleton.toJSON' - }, - bones: [], - boneInverses: [] - }; + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - data.uuid = this.uuid; + currentRenderList = renderLists.get( scene, renderListStack.length ); + currentRenderList.init(); - const bones = this.bones; - const boneInverses = this.boneInverses; + renderListStack.push( currentRenderList ); - for ( let i = 0, l = bones.length; i < l; i ++ ) { + projectObject( scene, camera, 0, _this.sortObjects ); - const bone = bones[ i ]; - data.bones.push( bone.uuid ); + currentRenderList.finish(); - const boneInverse = boneInverses[ i ]; - data.boneInverses.push( boneInverse.toArray() ); + if ( _this.sortObjects === true ) { + + currentRenderList.sort( _opaqueSort, _transparentSort ); } - return data; + // - } + if ( _clippingEnabled === true ) clipping.beginShadows(); - } + const shadowsArray = currentRenderState.state.shadowsArray; - const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); - const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); + shadowMap.render( shadowsArray, scene, camera ); - const _instanceIntersects = []; + if ( _clippingEnabled === true ) clipping.endShadows(); - const _mesh = /*@__PURE__*/ new Mesh(); + // - class InstancedMesh extends Mesh { + if ( this.info.autoReset === true ) this.info.reset(); - constructor( geometry, material, count ) { + // - super( geometry, material ); + background.render( currentRenderList, scene ); - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; + // render scene - this.count = count; + currentRenderState.setupLights( _this.physicallyCorrectLights ); - this.frustumCulled = false; + if ( camera.isArrayCamera ) { - } + const cameras = camera.cameras; - copy( source ) { + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - super.copy( source ); + const camera2 = cameras[ i ]; - this.instanceMatrix.copy( source.instanceMatrix ); + renderScene( currentRenderList, scene, camera2, camera2.viewport ); - if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); + } - this.count = source.count; + } else { - return this; + renderScene( currentRenderList, scene, camera ); - } + } - getColorAt( index, color ) { + // - color.fromArray( this.instanceColor.array, index * 3 ); + if ( _currentRenderTarget !== null ) { - } + // resolve multisample renderbuffers to a single-sample texture if necessary - getMatrixAt( index, matrix ) { + textures.updateMultisampleRenderTarget( _currentRenderTarget ); - matrix.fromArray( this.instanceMatrix.array, index * 16 ); + // Generate mipmap if we're using any kind of mipmap filtering - } + textures.updateRenderTargetMipmap( _currentRenderTarget ); - raycast( raycaster, intersects ) { + } - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; + // - _mesh.geometry = this.geometry; - _mesh.material = this.material; + if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - if ( _mesh.material === undefined ) return; + // _gl.finish(); - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { + bindingStates.resetDefaultState(); + _currentMaterialId = - 1; + _currentCamera = null; - // calculate the world matrix for each instance + renderStateStack.pop(); - this.getMatrixAt( instanceId, _instanceLocalMatrix ); + if ( renderStateStack.length > 0 ) { - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); + currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; - // the mesh represents this single instance + } else { - _mesh.matrixWorld = _instanceWorldMatrix; + currentRenderState = null; - _mesh.raycast( raycaster, _instanceIntersects ); + } - // process the result of raycast + renderListStack.pop(); - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { + if ( renderListStack.length > 0 ) { - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); + currentRenderList = renderListStack[ renderListStack.length - 1 ]; - } + } else { - _instanceIntersects.length = 0; + currentRenderList = null; } - } + }; - setColorAt( index, color ) { + function projectObject( object, camera, groupOrder, sortObjects ) { - if ( this.instanceColor === null ) { + if ( object.visible === false ) return; - this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 ); + const visible = object.layers.test( camera.layers ); - } + if ( visible ) { - color.toArray( this.instanceColor.array, index * 3 ); + if ( object.isGroup ) { - } + groupOrder = object.renderOrder; - setMatrixAt( index, matrix ) { + } else if ( object.isLOD ) { - matrix.toArray( this.instanceMatrix.array, index * 16 ); + if ( object.autoUpdate === true ) object.update( camera ); - } + } else if ( object.isLight ) { - updateMorphTargets() { + currentRenderState.pushLight( object ); - } + if ( object.castShadow ) { - dispose() { + currentRenderState.pushShadow( object ); - this.dispatchEvent( { type: 'dispose' } ); + } - } + } else if ( object.isSprite ) { - } + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - InstancedMesh.prototype.isInstancedMesh = true; + if ( sortObjects ) { - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - class LineBasicMaterial extends Material { + } - constructor( parameters ) { + const geometry = objects.update( object ); + const material = object.material; - super(); + if ( material.visible ) { - this.type = 'LineBasicMaterial'; + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - this.color = new Color( 0xffffff ); + } - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; + } - this.morphTargets = false; + } else if ( object.isMesh || object.isLine || object.isPoints ) { - this.setValues( parameters ); + if ( object.isSkinnedMesh ) { - } + // update skeleton only once in a frame + if ( object.skeleton.frame !== info.render.frame ) { - copy( source ) { + object.skeleton.update(); + object.skeleton.frame = info.render.frame; - super.copy( source ); + } - this.color.copy( source.color ); + } - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - this.morphTargets = source.morphTargets; + if ( sortObjects ) { - return this; + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - } + } - } + const geometry = objects.update( object ); + const material = object.material; - LineBasicMaterial.prototype.isLineBasicMaterial = true; + if ( Array.isArray( material ) ) { - const _start$1 = /*@__PURE__*/ new Vector3(); - const _end$1 = /*@__PURE__*/ new Vector3(); - const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); - const _ray$1 = /*@__PURE__*/ new Ray(); - const _sphere$1 = /*@__PURE__*/ new Sphere(); + const groups = geometry.groups; - class Line extends Object3D { + for ( let i = 0, l = groups.length; i < l; i ++ ) { - constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - super(); + if ( groupMaterial && groupMaterial.visible ) { - this.type = 'Line'; + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - this.geometry = geometry; - this.material = material; + } - this.updateMorphTargets(); + } - } + } else if ( material.visible ) { - copy( source ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - super.copy( source ); + } - this.material = source.material; - this.geometry = source.geometry; + } - return this; + } - } + } - computeLineDistances() { + const children = object.children; - const geometry = this.geometry; + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( geometry.isBufferGeometry ) { + projectObject( children[ i ], camera, groupOrder, sortObjects ); - // we assume non-indexed geometry + } - if ( geometry.index === null ) { + } - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; + function renderScene( currentRenderList, scene, camera, viewport ) { - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; + const transparentObjects = currentRenderList.transparent; - _start$1.fromBufferAttribute( positionAttribute, i - 1 ); - _end$1.fromBufferAttribute( positionAttribute, i ); + currentRenderState.setupLightsView( camera ); - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start$1.distanceTo( _end$1 ); + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera ); - } + if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); + if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); + if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - } else { + // Ensure depth buffer writing is enabled so it can be cleared on next render - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + state.buffers.depth.setTest( true ); + state.buffers.depth.setMask( true ); + state.buffers.color.setMask( true ); - } + state.setPolygonOffset( false ); - } else if ( geometry.isGeometry ) { + } - console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + function renderTransmissionPass( opaqueObjects, scene, camera ) { - } + const isWebGL2 = capabilities.isWebGL2; - return this; + if ( _transmissionRenderTarget === null ) { - } + _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { + generateMipmaps: true, + type: utils.convert( HalfFloatType ) !== null ? HalfFloatType : UnsignedByteType, + minFilter: LinearMipmapLinearFilter, + samples: ( isWebGL2 && _antialias === true ) ? 4 : 0 + } ); - raycast( raycaster, intersects ) { + } - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; - const drawRange = geometry.drawRange; + _this.getDrawingBufferSize( _vector2 ); - // Checking boundingSphere distance to ray + if ( isWebGL2 ) { - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); - _sphere$1.copy( geometry.boundingSphere ); - _sphere$1.applyMatrix4( matrixWorld ); - _sphere$1.radius += threshold; + } else { - if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; + _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); + + } // - _inverseMatrix$1.copy( matrixWorld ).invert(); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); + const currentRenderTarget = _this.getRenderTarget(); + _this.setRenderTarget( _transmissionRenderTarget ); + _this.clear(); - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + // Turn off the features which can affect the frag color for opaque objects pass. + // Otherwise they are applied twice in opaque objects pass and transmission objects pass. + const currentToneMapping = _this.toneMapping; + _this.toneMapping = NoToneMapping; - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = this.isLineSegments ? 2 : 1; + renderObjects( opaqueObjects, scene, camera ); - if ( geometry.isBufferGeometry ) { + _this.toneMapping = currentToneMapping; - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - if ( index !== null ) { + _this.setRenderTarget( currentRenderTarget ); - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + } - for ( let i = start, l = end - 1; i < l; i += step ) { + function renderObjects( renderList, scene, camera ) { - const a = index.getX( i ); - const b = index.getX( i + 1 ); + const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - vStart.fromBufferAttribute( positionAttribute, a ); - vEnd.fromBufferAttribute( positionAttribute, b ); + for ( let i = 0, l = renderList.length; i < l; i ++ ) { - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + const renderItem = renderList[ i ]; - if ( distSq > localThresholdSq ) continue; + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = overrideMaterial === null ? renderItem.material : overrideMaterial; + const group = renderItem.group; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + if ( object.layers.test( camera.layers ) ) { - const distance = raycaster.ray.origin.distanceTo( interRay ); + renderObject( object, scene, camera, geometry, material, group ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + } - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } - } ); + function renderObject( object, scene, camera, geometry, material, group ) { - } + object.onBeforeRender( _this, scene, camera, geometry, material, group ); - } else { + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); + material.onBeforeRender( _this, scene, camera, geometry, object, group ); - for ( let i = start, l = end - 1; i < l; i += step ) { + if ( material.transparent === true && material.side === DoubleSide ) { - vStart.fromBufferAttribute( positionAttribute, i ); - vEnd.fromBufferAttribute( positionAttribute, i + 1 ); + material.side = BackSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + material.side = FrontSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( distSq > localThresholdSq ) continue; + material.side = DoubleSide; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } else { - const distance = raycaster.ray.origin.distanceTo( interRay ); + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + object.onAfterRender( _this, scene, camera, geometry, material, group ); - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } - } ); + function getProgram( material, scene, object ) { - } + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - } + const materialProperties = properties.get( material ); - } else if ( geometry.isGeometry ) { + const lights = currentRenderState.state.lights; + const shadowsArray = currentRenderState.state.shadowsArray; - console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + const lightsStateVersion = lights.state.version; - } + const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); + const programCacheKey = programCache.getProgramCacheKey( parameters ); - } + let programs = materialProperties.programs; - updateMorphTargets() { + // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - const geometry = this.geometry; + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - if ( geometry.isBufferGeometry ) { + if ( programs === undefined ) { - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + // new material - if ( keys.length > 0 ) { + material.addEventListener( 'dispose', onMaterialDispose ); - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + programs = new Map(); + materialProperties.programs = programs; - if ( morphAttribute !== undefined ) { + } - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + let program = programs.get( programCacheKey ); - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + if ( program !== undefined ) { - const name = morphAttribute[ m ].name || String( m ); + // early out if program and light state is identical - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - } + updateCommonMaterialProperties( material, parameters ); - } + return program; } } else { - const morphTargets = geometry.morphTargets; + parameters.uniforms = programCache.getUniforms( material ); - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + material.onBuild( object, parameters, _this ); - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + material.onBeforeCompile( parameters, _this ); - } + program = programCache.acquireProgram( parameters, programCacheKey ); + programs.set( programCacheKey, program ); - } + materialProperties.uniforms = parameters.uniforms; - } + } - } + const uniforms = materialProperties.uniforms; - Line.prototype.isLine = true; + if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { - const _start = /*@__PURE__*/ new Vector3(); - const _end = /*@__PURE__*/ new Vector3(); + uniforms.clippingPlanes = clipping.uniform; - class LineSegments extends Line { + } - constructor( geometry, material ) { + updateCommonMaterialProperties( material, parameters ); - super( geometry, material ); + // store the light setup it was created for - this.type = 'LineSegments'; + materialProperties.needsLights = materialNeedsLights( material ); + materialProperties.lightsStateVersion = lightsStateVersion; - } + if ( materialProperties.needsLights ) { - computeLineDistances() { + // wire up the material to this renderer's lighting state - const geometry = this.geometry; + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.lightProbe.value = lights.state.probe; + uniforms.directionalLights.value = lights.state.directional; + uniforms.directionalLightShadows.value = lights.state.directionalShadow; + uniforms.spotLights.value = lights.state.spot; + uniforms.spotLightShadows.value = lights.state.spotShadow; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; - if ( geometry.isBufferGeometry ) { + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; + // TODO (abelnation): add area lights shadow info to uniforms - // we assume non-indexed geometry + } - if ( geometry.index === null ) { + const progUniforms = program.getUniforms(); + const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - const positionAttribute = geometry.attributes.position; - const lineDistances = []; + materialProperties.currentProgram = program; + materialProperties.uniformsList = uniformsList; - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { + return program; - _start.fromBufferAttribute( positionAttribute, i ); - _end.fromBufferAttribute( positionAttribute, i + 1 ); + } - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); + function updateCommonMaterialProperties( material, parameters ) { - } + const materialProperties = properties.get( material ); - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + materialProperties.outputEncoding = parameters.outputEncoding; + materialProperties.instancing = parameters.instancing; + materialProperties.skinning = parameters.skinning; + materialProperties.morphTargets = parameters.morphTargets; + materialProperties.morphNormals = parameters.morphNormals; + materialProperties.morphColors = parameters.morphColors; + materialProperties.morphTargetsCount = parameters.morphTargetsCount; + materialProperties.numClippingPlanes = parameters.numClippingPlanes; + materialProperties.numIntersection = parameters.numClipIntersection; + materialProperties.vertexAlphas = parameters.vertexAlphas; + materialProperties.vertexTangents = parameters.vertexTangents; + materialProperties.toneMapping = parameters.toneMapping; - } else { + } - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + function setProgram( camera, scene, geometry, material, object ) { - } + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - } else if ( geometry.isGeometry ) { + textures.resetTextureUnits(); - console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; + const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding ); + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; + const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; + const morphTargets = !! geometry.morphAttributes.position; + const morphNormals = !! geometry.morphAttributes.normal; + const morphColors = !! geometry.morphAttributes.color; + const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; + + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - } + const materialProperties = properties.get( material ); + const lights = currentRenderState.state.lights; - return this; + if ( _clippingEnabled === true ) { - } + if ( _localClippingEnabled === true || camera !== _currentCamera ) { - } + const useCache = + camera === _currentCamera && + material.id === _currentMaterialId; - LineSegments.prototype.isLineSegments = true; + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + clipping.setState( material, camera, useCache ); - class LineLoop extends Line { + } - constructor( geometry, material ) { + } - super( geometry, material ); + // - this.type = 'LineLoop'; + let needsProgramChange = false; - } + if ( material.version === materialProperties.__version ) { - } + if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - LineLoop.prototype.isLineLoop = true; + needsProgramChange = true; - /** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ + } else if ( materialProperties.outputEncoding !== encoding ) { - class PointsMaterial extends Material { + needsProgramChange = true; - constructor( parameters ) { + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - super(); + needsProgramChange = true; - this.type = 'PointsMaterial'; + } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - this.color = new Color( 0xffffff ); + needsProgramChange = true; - this.map = null; + } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - this.alphaMap = null; + needsProgramChange = true; - this.size = 1; - this.sizeAttenuation = true; + } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - this.morphTargets = false; + needsProgramChange = true; - this.setValues( parameters ); + } else if ( materialProperties.envMap !== envMap ) { - } + needsProgramChange = true; - copy( source ) { + } else if ( material.fog && materialProperties.fog !== fog ) { - super.copy( source ); + needsProgramChange = true; - this.color.copy( source.color ); + } else if ( materialProperties.numClippingPlanes !== undefined && + ( materialProperties.numClippingPlanes !== clipping.numPlanes || + materialProperties.numIntersection !== clipping.numIntersection ) ) { - this.map = source.map; + needsProgramChange = true; - this.alphaMap = source.alphaMap; + } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; + needsProgramChange = true; - this.morphTargets = source.morphTargets; + } else if ( materialProperties.vertexTangents !== vertexTangents ) { - return this; + needsProgramChange = true; - } + } else if ( materialProperties.morphTargets !== morphTargets ) { - } + needsProgramChange = true; - PointsMaterial.prototype.isPointsMaterial = true; + } else if ( materialProperties.morphNormals !== morphNormals ) { - const _inverseMatrix = /*@__PURE__*/ new Matrix4(); - const _ray = /*@__PURE__*/ new Ray(); - const _sphere = /*@__PURE__*/ new Sphere(); - const _position$2 = /*@__PURE__*/ new Vector3(); + needsProgramChange = true; - class Points extends Object3D { + } else if ( materialProperties.morphColors !== morphColors ) { - constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { + needsProgramChange = true; - super(); + } else if ( materialProperties.toneMapping !== toneMapping ) { - this.type = 'Points'; + needsProgramChange = true; - this.geometry = geometry; - this.material = material; + } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { - this.updateMorphTargets(); + needsProgramChange = true; - } + } - copy( source ) { + } else { - super.copy( source ); + needsProgramChange = true; + materialProperties.__version = material.version; - this.material = source.material; - this.geometry = source.geometry; + } - return this; + // - } + let program = materialProperties.currentProgram; - raycast( raycaster, intersects ) { + if ( needsProgramChange === true ) { - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; - const drawRange = geometry.drawRange; + program = getProgram( material, scene, object ); - // Checking boundingSphere distance to ray + } - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + let refreshProgram = false; + let refreshMaterial = false; + let refreshLights = false; - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); - _sphere.radius += threshold; + const p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; + if ( state.useProgram( program.program ) ) { - // + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; - _inverseMatrix.copy( matrixWorld ).invert(); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); + } - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + if ( material.id !== _currentMaterialId ) { - if ( geometry.isBufferGeometry ) { + _currentMaterialId = material.id; - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + refreshMaterial = true; - if ( index !== null ) { + } - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + if ( refreshProgram || _currentCamera !== camera ) { - for ( let i = start, il = end; i < il; i ++ ) { + p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - const a = index.getX( i ); + if ( capabilities.logarithmicDepthBuffer ) { - _position$2.fromBufferAttribute( positionAttribute, a ); + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); + } - } + if ( _currentCamera !== camera ) { - } else { + _currentCamera = camera; - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: - for ( let i = start, l = end; i < l; i ++ ) { + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done - _position$2.fromBufferAttribute( positionAttribute, i ); + } - testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - } + if ( material.isShaderMaterial || + material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshStandardMaterial || + material.envMap ) { - } + const uCamPos = p_uniforms.map.cameraPosition; - } else { + if ( uCamPos !== undefined ) { - console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + uCamPos.setValue( _gl, + _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - } + } - } + } - updateMorphTargets() { + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial ) { - const geometry = this.geometry; + p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - if ( geometry.isBufferGeometry ) { + } - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial || + material.isShadowMaterial || + object.isSkinnedMesh ) { - if ( keys.length > 0 ) { + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + } - if ( morphAttribute !== undefined ) { + } - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + // skinning and morph target uniforms must be set even if material didn't change + // auto-setting of texture unit for bone and morph texture must go before other textures + // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + if ( object.isSkinnedMesh ) { - const name = morphAttribute[ m ].name || String( m ); + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + const skeleton = object.skeleton; - } + if ( skeleton ) { - } + if ( capabilities.floatVertexTextures ) { - } + if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - } else { + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); + p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - const morphTargets = geometry.morphTargets; + } else { - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + } } } - } + const morphAttributes = geometry.morphAttributes; - } + if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { - Points.prototype.isPoints = true; + morphtargets.update( object, geometry, material, program ); - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { + } - const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - if ( rayPointDistanceSq < localThresholdSq ) { + if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - const intersectPoint = new Vector3(); + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - _ray.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); + } - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); + if ( refreshMaterial ) { - if ( distance < raycaster.near || distance > raycaster.far ) return; + p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - intersects.push( { + if ( materialProperties.needsLights ) { - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object + // the current material requires lighting info - } ); + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required - } + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - } + } - class VideoTexture extends Texture { + // refresh uniforms common to several materials - constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + if ( fog && material.fog ) { - super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + materials.refreshFogUniforms( m_uniforms, fog ); - this.format = format !== undefined ? format : RGBFormat; + } - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - this.generateMipmaps = false; + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - const scope = this; + } - function updateVideo() { + if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; } - if ( 'requestVideoFrameCallback' in video ) { + if ( material.isSpriteMaterial ) { - video.requestVideoFrameCallback( updateVideo ); + p_uniforms.setValue( _gl, 'center', object.center ); } - } + // common matrices - clone() { + p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); + p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - return new this.constructor( this.image ).copy( this ); + return program; } - update() { - - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { + function markUniformsLightsNeedsUpdate( uniforms, value ) { - this.needsUpdate = true; + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; - } + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; } - } - - VideoTexture.prototype.isVideoTexture = true; - - class CompressedTexture extends Texture { - - constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + function materialNeedsLights( material ) { - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || + material.isMeshStandardMaterial || material.isShadowMaterial || + ( material.isShaderMaterial && material.lights === true ); - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + } - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) + this.getActiveCubeFace = function () { - this.flipY = false; + return _currentActiveCubeFace; - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + }; - this.generateMipmaps = false; + this.getActiveMipmapLevel = function () { - } + return _currentActiveMipmapLevel; - } + }; - CompressedTexture.prototype.isCompressedTexture = true; + this.getRenderTarget = function () { - class CanvasTexture extends Texture { + return _currentRenderTarget; - constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + }; - super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { - this.needsUpdate = true; + properties.get( renderTarget.texture ).__webglTexture = colorTexture; + properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; - } + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__hasExternalTextures = true; - } + if ( renderTargetProperties.__hasExternalTextures ) { - CanvasTexture.prototype.isCanvasTexture = true; + renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; - class DepthTexture extends Texture { + if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { - constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { + // The multisample_render_to_texture extension doesn't work properly if there + // are midframe flushes and an external depth buffer. Disable use of the extension. + if ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) { - format = format !== undefined ? format : DepthFormat; + console.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' ); + renderTargetProperties.__useRenderToTexture = false; - if ( format !== DepthFormat && format !== DepthStencilFormat ) { + } - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); + } } - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; + }; - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { - this.image = { width: width, height: height }; + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__webglFramebuffer = defaultFramebuffer; + renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + }; - this.flipY = false; - this.generateMipmaps = false; + this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - } + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; + let useDefaultFramebuffer = true; - } + if ( renderTarget ) { - DepthTexture.prototype.isDepthTexture = true; + const renderTargetProperties = properties.get( renderTarget ); - new Vector3(); - new Vector3(); - new Vector3(); - new Triangle(); + if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { - /** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ + // We need to make sure to rebind the framebuffer. + state.bindFramebuffer( 36160, null ); + useDefaultFramebuffer = false; - const Earcut = { + } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { - triangulate: function ( data, holeIndices, dim ) { + textures.setupRenderTarget( renderTarget ); - dim = dim || 2; + } else if ( renderTargetProperties.__hasExternalTextures ) { - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList( data, 0, outerLen, dim, true ); - const triangles = []; + // Color and depth texture must be rebound in order for the swapchain to update. + textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; + } - let minX, minY, maxX, maxY, x, y, invSize; + } - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); + let framebuffer = null; + let isCube = false; + let isRenderTarget3D = false; - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { + if ( renderTarget ) { - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; + const texture = renderTarget.texture; - for ( let i = dim; i < outerLen; i += dim ) { + if ( texture.isData3DTexture || texture.isDataArrayTexture ) { - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; + isRenderTarget3D = true; } - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; + const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - } + if ( renderTarget.isWebGLCubeRenderTarget ) { - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); + framebuffer = __webglFramebuffer[ activeCubeFace ]; + isCube = true; - return triangles; + } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { - } + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - }; + } else { - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { + framebuffer = __webglFramebuffer; - let i, last; + } - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + } else { - } else { + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); + _currentScissorTest = _scissorTest; - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + } - } + const framebufferBound = state.bindFramebuffer( 36160, framebuffer ); - if ( last && equals( last, last.next ) ) { + if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { - removeNode( last ); - last = last.next; + state.drawBuffers( renderTarget, framebuffer ); - } + } - return last; + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); - } + if ( isCube ) { - // eliminate colinear or duplicate points - function filterPoints( start, end ) { + const textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - if ( ! start ) return start; - if ( ! end ) end = start; + } else if ( isRenderTarget3D ) { - let p = start, - again; - do { + const textureProperties = properties.get( renderTarget.texture ); + const layer = activeCubeFace || 0; + _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - again = false; + } - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { + _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; + }; - } else { + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - p = p.next; + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; } - } while ( again || p !== end ); + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - return end; + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - } + framebuffer = framebuffer[ activeCubeFaceIndex ]; - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { + } - if ( ! ear ) return; + if ( framebuffer ) { - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); + state.bindFramebuffer( 36160, framebuffer ); - let stop = ear, - prev, next; + try { - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; - prev = ear.prev; - next = ear.next; + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); + } - removeNode( ear ); + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - continue; + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - } + } - ear = next; + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - // try filtering points and slicing again - if ( ! pass ) { + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); + } - // if this didn't work, try curing all small self-intersections locally + } finally { - } else if ( pass === 1 ) { + // restore framebuffer of current render target if necessary - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( 36160, framebuffer ); - // as a last resort, try splitting the remaining polygon into two + } - } else if ( pass === 2 ) { + } - splitEarcut( ear, triangles, dim, minX, minY, invSize ); + }; - } + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - break; + if ( texture.isFramebufferTexture !== true ) { - } + console.error( 'THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture.' ); + return; - } + } - } + const levelScale = Math.pow( 2, - level ); + const width = Math.floor( texture.image.width * levelScale ); + const height = Math.floor( texture.image.height * levelScale ); - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { + textures.setTexture2D( texture, 0 ); - const a = ear.prev, - b = ear, - c = ear.next; + _gl.copyTexSubImage2D( 3553, level, 0, 0, position.x, position.y, width, height ); - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + state.unbindTexture(); - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; + }; - while ( p !== ear.prev ) { + this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; + const width = srcTexture.image.width; + const height = srcTexture.image.height; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); - } + textures.setTexture2D( dstTexture, 0 ); - return true; + // As another texture upload may have changed pixelStorei + // parameters, make sure they are correct for the dstTexture + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - } + if ( srcTexture.isDataTexture ) { - function isEarHashed( ear, minX, minY, invSize ) { + _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - const a = ear.prev, - b = ear, - c = ear.next; + } else { - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + if ( srcTexture.isCompressedTexture ) { - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); + _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - // z-order range for the current triangle bbox; - const minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); + } else { - let p = ear.prevZ, - n = ear.nextZ; + _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { + } - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + } - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - } + state.unbindTexture(); - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { + }; - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { - } + if ( _this.isWebGL1Renderer ) { - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); + return; - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + } - } + const width = sourceBox.max.x - sourceBox.min.x + 1; + const height = sourceBox.max.y - sourceBox.min.y + 1; + const depth = sourceBox.max.z - sourceBox.min.z + 1; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); + let glTarget; - return true; + if ( dstTexture.isData3DTexture ) { - } + textures.setTexture3D( dstTexture, 0 ); + glTarget = 32879; - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { + } else if ( dstTexture.isDataArrayTexture ) { - let p = start; - do { + textures.setTexture2DArray( dstTexture, 0 ); + glTarget = 35866; - const a = p.prev, - b = p.next.next; + } else { - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); - - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); - - p = start = b; + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); + return; } - p = p.next; + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - } while ( p !== start ); + const unpackRowLen = _gl.getParameter( 3314 ); + const unpackImageHeight = _gl.getParameter( 32878 ); + const unpackSkipPixels = _gl.getParameter( 3316 ); + const unpackSkipRows = _gl.getParameter( 3315 ); + const unpackSkipImages = _gl.getParameter( 32877 ); - return filterPoints( p ); + const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; - } + _gl.pixelStorei( 3314, image.width ); + _gl.pixelStorei( 32878, image.height ); + _gl.pixelStorei( 3316, sourceBox.min.x ); + _gl.pixelStorei( 3315, sourceBox.min.y ); + _gl.pixelStorei( 32877, sourceBox.min.z ); - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { + if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { - // look for a valid diagonal that divides the polygon into two - let a = start; - do { + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - let b = a.next.next; - while ( b !== a.prev ) { + } else { - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { + if ( srcTexture.isCompressedTexture ) { - // split the polygon in two by the diagonal - let c = splitPolygon( a, b ); + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); + _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); + } else { - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } - b = b.next; - } - a = a.next; - - } while ( a !== start ); + _gl.pixelStorei( 3314, unpackRowLen ); + _gl.pixelStorei( 32878, unpackImageHeight ); + _gl.pixelStorei( 3316, unpackSkipPixels ); + _gl.pixelStorei( 3315, unpackSkipRows ); + _gl.pixelStorei( 32877, unpackSkipImages ); - } + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { + state.unbindTexture(); - const queue = []; - let i, len, start, end, list; + }; - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { + this.initTexture = function ( texture ) { - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); + textures.setTexture2D( texture, 0 ); - } + state.unbindTexture(); - queue.sort( compareX ); + }; - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { + this.resetState = function () { - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); + _currentActiveCubeFace = 0; + _currentActiveMipmapLevel = 0; + _currentRenderTarget = null; - } + state.reset(); + bindingStates.reset(); - return outerNode; + }; - } + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - function compareX( a, b ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - return a.x - b.x; + } } - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { + WebGLRenderer.prototype.isWebGLRenderer = true; - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { + class WebGL1Renderer extends WebGLRenderer {} - const b = splitPolygon( outerNode, hole ); + WebGL1Renderer.prototype.isWebGL1Renderer = true; - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); + class Scene extends Object3D { - } + constructor() { - } + super(); - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { + this.type = 'Scene'; - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; + this.background = null; + this.environment = null; + this.fog = null; - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { + this.overrideMaterial = null; - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { + this.autoUpdate = true; // checked by the renderer - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - qx = x; - if ( x === hx ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; + } - } + } - m = p.x < p.next.x ? p : p.next; + copy( source, recursive ) { - } + super.copy( source, recursive ); - } + if ( source.background !== null ) this.background = source.background.clone(); + if ( source.environment !== null ) this.environment = source.environment.clone(); + if ( source.fog !== null ) this.fog = source.fog.clone(); - p = p.next; + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - } while ( p !== outerNode ); + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - if ( ! m ) return null; + return this; - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint + } - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point + toJSON( meta ) { - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; + const data = super.toJSON( meta ); - p = m; + if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - do { + return data; - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { + } - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential + } - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { + Scene.prototype.isScene = true; - m = p; - tanMin = tan; + class InterleavedBuffer { - } + constructor( array, stride ) { - } + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; - p = p.next; + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; - } while ( p !== stop ); + this.version = 0; - return m; + this.uuid = generateUUID(); - } + } - // whether sector in vertex m contains sector in vertex p in the same coordinates - function sectorContainsSector( m, p ) { + onUploadCallback() {} - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; + set needsUpdate( value ) { - } + if ( value === true ) this.version ++; - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { + } - let p = start; - do { + setUsage( value ) { - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; + this.usage = value; - } while ( p !== start ); + return this; - p.prevZ.nextZ = null; - p.prevZ = null; + } - sortLinked( p ); + copy( source ) { - } + this.array = new source.array.constructor( source.array ); + this.count = source.count; + this.stride = source.stride; + this.usage = source.usage; - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { + return this; - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; + } - do { + copyAt( index1, attribute, index2 ) { - p = list; - list = null; - tail = null; - numMerges = 0; + index1 *= this.stride; + index2 *= attribute.stride; - while ( p ) { + for ( let i = 0, l = this.stride; i < l; i ++ ) { - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - pSize ++; - q = q.nextZ; - if ( ! q ) break; + } - } + return this; - qSize = inSize; + } - while ( pSize > 0 || ( qSize > 0 && q ) ) { + set( value, offset = 0 ) { - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { + this.array.set( value, offset ); - e = p; - p = p.nextZ; - pSize --; + return this; - } else { + } - e = q; - q = q.nextZ; - qSize --; + clone( data ) { - } + if ( data.arrayBuffers === undefined ) { - if ( tail ) tail.nextZ = e; - else list = e; + data.arrayBuffers = {}; - e.prevZ = tail; - tail = e; + } - } + if ( this.array.buffer._uuid === undefined ) { - p = q; + this.array.buffer._uuid = generateUUID(); } - tail.nextZ = null; - inSize *= 2; + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - } while ( numMerges > 1 ); + data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - return list; + } - } + const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { + const ib = new this.constructor( array, this.stride ); + ib.setUsage( this.usage ); - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; + return ib; - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; + } - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; + onUpload( callback ) { - return x | ( y << 1 ); + this.onUploadCallback = callback; - } + return this; - // find the leftmost node of a polygon ring - function getLeftmost( start ) { + } - let p = start, - leftmost = start; - do { + toJSON( data ) { - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; + if ( data.arrayBuffers === undefined ) { - } while ( p !== start ); + data.arrayBuffers = {}; - return leftmost; + } - } + // generate UUID for array buffer if necessary - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { + if ( this.array.buffer._uuid === undefined ) { - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; + this.array.buffer._uuid = generateUUID(); - } + } - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case + data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); - } + } - // signed area of a triangle - function area( p, q, r ) { + // - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); + return { + uuid: this.uuid, + buffer: this.array.buffer._uuid, + type: this.array.constructor.name, + stride: this.stride + }; + + } } - // check if two points are equal - function equals( p1, p2 ) { + InterleavedBuffer.prototype.isInterleavedBuffer = true; - return p1.x === p2.x && p1.y === p2.y; + const _vector$6 = /*@__PURE__*/ new Vector3(); - } + class InterleavedBufferAttribute { - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { + constructor( interleavedBuffer, itemSize, offset, normalized = false ) { - const o1 = sign( area( p1, q1, p2 ) ); - const o2 = sign( area( p1, q1, q2 ) ); - const o3 = sign( area( p2, q2, p1 ) ); - const o4 = sign( area( p2, q2, q1 ) ); + this.name = ''; - if ( o1 !== o2 && o3 !== o4 ) return true; // general case + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + this.normalized = normalized === true; - return false; + } - } + get count() { - // for collinear points p, q, r, check if point q lies on segment pr - function onSegment( p, q, r ) { + return this.data.count; - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); + } - } + get array() { - function sign( num ) { + return this.data.array; - return num > 0 ? 1 : num < 0 ? - 1 : 0; + } - } + set needsUpdate( value ) { - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { + this.data.needsUpdate = value; - let p = a; - do { + } - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; + applyMatrix4( m ) { - } while ( p !== a ); + for ( let i = 0, l = this.data.count; i < l; i ++ ) { - return false; + _vector$6.fromBufferAttribute( this, i ); - } + _vector$6.applyMatrix4( m ); - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; + } - } + return this; - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { + } - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { + applyNormalMatrix( m ) { - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; + for ( let i = 0, l = this.count; i < l; i ++ ) { - } while ( p !== a ); + _vector$6.fromBufferAttribute( this, i ); - return inside; + _vector$6.applyNormalMatrix( m ); - } + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { + } - const a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; + return this; - a.next = b; - b.prev = a; + } - a2.next = an; - an.prev = a2; + transformDirection( m ) { - b2.next = a2; - a2.prev = b2; + for ( let i = 0, l = this.count; i < l; i ++ ) { - bp.next = b2; - b2.prev = bp; + _vector$6.fromBufferAttribute( this, i ); - return b2; + _vector$6.transformDirection( m ); - } + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { + } - const p = new Node( i, x, y ); + return this; - if ( ! last ) { + } - p.prev = p; - p.next = p; + setX( index, x ) { - } else { + this.data.array[ index * this.data.stride + this.offset ] = x; - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; + return this; } - return p; + setY( index, y ) { - } + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - function removeNode( p ) { + return this; - p.next.prev = p.prev; - p.prev.next = p.next; + } - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; + setZ( index, z ) { - } + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - function Node( i, x, y ) { + return this; - // vertex index in coordinates array - this.i = i; + } - // vertex coordinates - this.x = x; - this.y = y; + setW( index, w ) { - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - // z-order curve value - this.z = null; + return this; - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; + } - // indicates whether this is a steiner point - this.steiner = false; + getX( index ) { - } + return this.data.array[ index * this.data.stride + this.offset ]; - function signedArea( data, start, end, dim ) { + } - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { + getY( index ) { - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; + return this.data.array[ index * this.data.stride + this.offset + 1 ]; } - return sum; + getZ( index ) { - } + return this.data.array[ index * this.data.stride + this.offset + 2 ]; - class ShapeUtils { + } - // calculate area of the contour polygon + getW( index ) { - static area( contour ) { + return this.data.array[ index * this.data.stride + this.offset + 3 ]; - const n = contour.length; - let a = 0.0; + } - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { + setXY( index, x, y ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + index = index * this.data.stride + this.offset; - } + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; - return a * 0.5; + return this; } - static isClockWise( pts ) { + setXYZ( index, x, y, z ) { - return ShapeUtils.area( pts ) < 0; + index = index * this.data.stride + this.offset; - } + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; - static triangulateShape( contour, holes ) { + return this; - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + } - removeDupEndPts( contour ); - addContour( vertices, contour ); + setXYZW( index, x, y, z, w ) { - // + index = index * this.data.stride + this.offset; - let holeIndex = contour.length; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; - holes.forEach( removeDupEndPts ); + return this; - for ( let i = 0; i < holes.length; i ++ ) { + } - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); + clone( data ) { - } + if ( data === undefined ) { - // + console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - const triangles = Earcut.triangulate( vertices, holeIndices ); + const array = []; - // + for ( let i = 0; i < this.count; i ++ ) { - for ( let i = 0; i < triangles.length; i += 3 ) { + const index = i * this.data.stride + this.offset; - faces.push( triangles.slice( i, i + 3 ) ); + for ( let j = 0; j < this.itemSize; j ++ ) { - } + array.push( this.data.array[ index + j ] ); - return faces; + } - } + } - } + return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - function removeDupEndPts( points ) { + } else { - const l = points.length; + if ( data.interleavedBuffers === undefined ) { - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + data.interleavedBuffers = {}; - points.pop(); + } - } + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - } + data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); - function addContour( vertices, contour ) { + } - for ( let i = 0; i < contour.length; i ++ ) { + return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); + } } - } + toJSON( data ) { - /** - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ + if ( data === undefined ) { - class ExtrudeGeometry extends BufferGeometry { + console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - constructor( shapes, options ) { + const array = []; - super(); + for ( let i = 0; i < this.count; i ++ ) { - this.type = 'ExtrudeGeometry'; + const index = i * this.data.stride + this.offset; - this.parameters = { - shapes: shapes, - options: options - }; + for ( let j = 0; j < this.itemSize; j ++ ) { - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + array.push( this.data.array[ index + j ] ); - const scope = this; + } - const verticesArray = []; - const uvArray = []; + } - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + // deinterleave data and save it as an ordinary buffer attribute for now - const shape = shapes[ i ]; - addShape( shape ); + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: array, + normalized: this.normalized + }; - } + } else { - // build geometry + // save as true interlaved attribtue - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); + if ( data.interleavedBuffers === undefined ) { - this.computeVertexNormals(); + data.interleavedBuffers = {}; - // functions + } - function addShape( shape ) { + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - const placeholder = []; + data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - // options + } - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; + return { + isInterleavedBufferAttribute: true, + itemSize: this.itemSize, + data: this.data.uuid, + offset: this.offset, + normalized: this.normalized + }; - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + } - const extrudePath = options.extrudePath; + } - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; + } - // deprecated options + InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; - if ( options.amount !== undefined ) { + class SpriteMaterial extends Material { - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; + constructor( parameters ) { - } + super(); - // + this.type = 'SpriteMaterial'; - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; + this.color = new Color( 0xffffff ); - if ( extrudePath ) { + this.map = null; - extrudePts = extrudePath.getSpacedPoints( steps ); + this.alphaMap = null; - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + this.rotation = 0; - // SETUP TNB variables + this.sizeAttenuation = true; - // TODO1 - have a .isClosed in spline? + this.transparent = true; - splineTube = extrudePath.computeFrenetFrames( steps, false ); + this.setValues( parameters ); - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + } - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); + copy( source ) { - } + super.copy( source ); - // Safeguards if bevels are not enabled + this.color.copy( source.color ); - if ( ! bevelEnabled ) { + this.map = source.map; - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; + this.alphaMap = source.alphaMap; - } + this.rotation = source.rotation; - // Variables initialization + this.sizeAttenuation = source.sizeAttenuation; - const shapePoints = shape.extractPoints( curveSegments ); + return this; - let vertices = shapePoints.shape; - const holes = shapePoints.holes; + } - const reverse = ! ShapeUtils.isClockWise( vertices ); + } - if ( reverse ) { + SpriteMaterial.prototype.isSpriteMaterial = true; - vertices = vertices.reverse(); + let _geometry; - // Maybe we should also check if holes are in the opposite direction, just to be safe ... + const _intersectPoint = /*@__PURE__*/ new Vector3(); + const _worldScale = /*@__PURE__*/ new Vector3(); + const _mvPosition = /*@__PURE__*/ new Vector3(); - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + const _alignedPosition = /*@__PURE__*/ new Vector2(); + const _rotatedPosition = /*@__PURE__*/ new Vector2(); + const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); - const ahole = holes[ h ]; + const _vA = /*@__PURE__*/ new Vector3(); + const _vB = /*@__PURE__*/ new Vector3(); + const _vC = /*@__PURE__*/ new Vector3(); - if ( ShapeUtils.isClockWise( ahole ) ) { + const _uvA = /*@__PURE__*/ new Vector2(); + const _uvB = /*@__PURE__*/ new Vector2(); + const _uvC = /*@__PURE__*/ new Vector2(); - holes[ h ] = ahole.reverse(); + class Sprite extends Object3D { - } + constructor( material ) { - } + super(); - } + this.type = 'Sprite'; + if ( _geometry === undefined ) { - const faces = ShapeUtils.triangulateShape( vertices, holes ); + _geometry = new BufferGeometry(); - /* Vertices */ + const float32Array = new Float32Array( [ + - 0.5, - 0.5, 0, 0, 0, + 0.5, - 0.5, 0, 1, 0, + 0.5, 0.5, 0, 1, 1, + - 0.5, 0.5, 0, 0, 1 + ] ); - const contour = vertices; // vertices has all points but contour has only points of circumference + const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); + _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); + _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - const ahole = holes[ h ]; + } - vertices = vertices.concat( ahole ); + this.geometry = _geometry; + this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - } + this.center = new Vector2( 0.5, 0.5 ); + } - function scalePt2( pt, vec, size ) { + raycast( raycaster, intersects ) { - if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); + if ( raycaster.camera === null ) { - return vec.clone().multiplyScalar( size ).add( pt ); + console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - } + } - const vlen = vertices.length, flen = faces.length; + _worldScale.setFromMatrixScale( this.matrixWorld ); + _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); + this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - // Find directions for point movement + _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - function getBevelVec( inPt, inPrev, inNext ) { + _worldScale.multiplyScalar( - _mvPosition.z ); - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. + } - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + const rotation = this.material.rotation; + let sin, cos; - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + if ( rotation !== 0 ) { - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; + cos = Math.cos( rotation ); + sin = Math.sin( rotation ); - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + } - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + const center = this.center; - if ( Math.abs( collinear0 ) > Number.EPSILON ) { + transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - // not collinear + _uvA.set( 0, 0 ); + _uvB.set( 1, 0 ); + _uvC.set( 1, 1 ); - // length of vectors for normalizing + // check first triangle + let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + if ( intersect === null ) { - // shift adjacent points by unit vectors to the left + // check second triangle + transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + _uvB.set( 0, 1 ); - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); + if ( intersect === null ) { - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + return; - // scaling factor for v_prev to intersection point + } - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + } - // vector from inPt to intersection point + const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + if ( distance < raycaster.near || distance > raycaster.far ) return; - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { + intersects.push( { - return new Vector2( v_trans_x, v_trans_y ); + distance: distance, + point: _intersectPoint.clone(), + uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), + face: null, + object: this - } else { + } ); - shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } - } + copy( source ) { - } else { + super.copy( source ); - // handle special case of collinear edges + if ( source.center !== undefined ) this.center.copy( source.center ); - let direction_eq = false; // assumes: opposite + this.material = source.material; - if ( v_prev_x > Number.EPSILON ) { + return this; - if ( v_next_x > Number.EPSILON ) { + } - direction_eq = true; + } - } + Sprite.prototype.isSprite = true; - } else { + function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - if ( v_prev_x < - Number.EPSILON ) { + // compute position in camera space + _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - if ( v_next_x < - Number.EPSILON ) { + // to check if rotation is not zero + if ( sin !== undefined ) { - direction_eq = true; + _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); + _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - } + } else { - } else { + _rotatedPosition.copy( _alignedPosition ); - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + } - direction_eq = true; - } + vertexPosition.copy( mvPosition ); + vertexPosition.x += _rotatedPosition.x; + vertexPosition.y += _rotatedPosition.y; - } + // transform to world space + vertexPosition.applyMatrix4( _viewWorldMatrix ); - } + } - if ( direction_eq ) { + const _basePosition = /*@__PURE__*/ new Vector3(); - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); + const _skinIndex = /*@__PURE__*/ new Vector4(); + const _skinWeight = /*@__PURE__*/ new Vector4(); - } else { + const _vector$5 = /*@__PURE__*/ new Vector3(); + const _matrix = /*@__PURE__*/ new Matrix4(); - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); + class SkinnedMesh extends Mesh { - } + constructor( geometry, material ) { - } + super( geometry, material ); - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + this.type = 'SkinnedMesh'; - } + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); + } - const contourMovements = []; + copy( source ) { - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + super.copy( source ); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + this.bindMode = source.bindMode; + this.bindMatrix.copy( source.bindMatrix ); + this.bindMatrixInverse.copy( source.bindMatrixInverse ); - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + this.skeleton = source.skeleton; - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + return this; - } + } - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); + bind( skeleton, bindMatrix ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + this.skeleton = skeleton; - const ahole = holes[ h ]; + if ( bindMatrix === undefined ) { - oneHoleMovements = []; + this.updateMatrixWorld( true ); - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + this.skeleton.calculateInverses(); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + bindMatrix = this.matrixWorld; - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + } - } + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.copy( bindMatrix ).invert(); - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + } - } + pose() { + this.skeleton.pose(); - // Loop bevelSegments, 1 for the front, 1 for the back + } - for ( let b = 0; b < bevelSegments; b ++ ) { + normalizeSkinWeights() { - //for ( b = bevelSegments; b > 0; b -- ) { + const vector = new Vector4(); - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + const skinWeight = this.geometry.attributes.skinWeight; - // contract shape + for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - for ( let i = 0, il = contour.length; i < il; i ++ ) { + vector.fromBufferAttribute( skinWeight, i ); - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + const scale = 1.0 / vector.manhattanLength(); - v( vert.x, vert.y, - z ); + if ( scale !== Infinity ) { - } + vector.multiplyScalar( scale ); - // expand holes + } else { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + vector.set( 1, 0, 0, 0 ); // do something reasonable - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + } - for ( let i = 0, il = ahole.length; i < il; i ++ ) { + skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + } - v( vert.x, vert.y, - z ); + } - } + updateMatrixWorld( force ) { - } + super.updateMatrixWorld( force ); - } + if ( this.bindMode === 'attached' ) { - const bs = bevelSize + bevelOffset; + this.bindMatrixInverse.copy( this.matrixWorld ).invert(); - // Back facing vertices + } else if ( this.bindMode === 'detached' ) { - for ( let i = 0; i < vlen; i ++ ) { + this.bindMatrixInverse.copy( this.bindMatrix ).invert(); - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + } else { - if ( ! extrudeByPath ) { + console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - v( vert.x, vert.y, 0 ); + } - } else { + } - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + boneTransform( index, target ) { - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + const skeleton = this.skeleton; + const geometry = this.geometry; - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); + _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - v( position2.x, position2.y, position2.z ); + _basePosition.copy( target ).applyMatrix4( this.bindMatrix ); - } + target.set( 0, 0, 0 ); - } + for ( let i = 0; i < 4; i ++ ) { - // Add stepped vertices... - // Including front facing vertices + const weight = _skinWeight.getComponent( i ); - for ( let s = 1; s <= steps; s ++ ) { + if ( weight !== 0 ) { - for ( let i = 0; i < vlen; i ++ ) { + const boneIndex = _skinIndex.getComponent( i ); - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - if ( ! extrudeByPath ) { + target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); - v( vert.x, vert.y, depth / steps * s ); + } - } else { + } - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + return target.applyMatrix4( this.bindMatrixInverse ); - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + } - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + } - v( position2.x, position2.y, position2.z ); + SkinnedMesh.prototype.isSkinnedMesh = true; - } + class Bone extends Object3D { - } + constructor() { - } + super(); + this.type = 'Bone'; - // Add bevel segments planes + } - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { + } - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + Bone.prototype.isBone = true; - // contract shape + class DataTexture extends Texture { - for ( let i = 0, il = contour.length; i < il; i ++ ) { + constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding ) { - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - } + this.image = { data: data, width: width, height: height }; - // expand holes + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + } - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + } - for ( let i = 0, il = ahole.length; i < il; i ++ ) { + DataTexture.prototype.isDataTexture = true; - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + const _offsetMatrix = /*@__PURE__*/ new Matrix4(); + const _identityMatrix = /*@__PURE__*/ new Matrix4(); - if ( ! extrudeByPath ) { + class Skeleton { - v( vert.x, vert.y, depth + z ); + constructor( bones = [], boneInverses = [] ) { - } else { + this.uuid = generateUUID(); - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + this.bones = bones.slice( 0 ); + this.boneInverses = boneInverses; + this.boneMatrices = null; - } + this.boneTexture = null; + this.boneTextureSize = 0; - } + this.frame = - 1; - } + this.init(); - } + } - /* Faces */ + init() { - // Top and bottom faces + const bones = this.bones; + const boneInverses = this.boneInverses; - buildLidFaces(); + this.boneMatrices = new Float32Array( bones.length * 16 ); - // Sides faces + // calculate inverse bone matrices if necessary - buildSideFaces(); + if ( boneInverses.length === 0 ) { + this.calculateInverses(); - ///// Internal functions + } else { - function buildLidFaces() { + // handle special case - const start = verticesArray.length / 3; + if ( bones.length !== boneInverses.length ) { - if ( bevelEnabled ) { + console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); - let layer = 0; // steps + 1 - let offset = vlen * layer; + this.boneInverses = []; - // Bottom faces + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - for ( let i = 0; i < flen; i ++ ) { + this.boneInverses.push( new Matrix4() ); - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + } - } + } - layer = steps + bevelSegments * 2; - offset = vlen * layer; + } - // Top faces + } - for ( let i = 0; i < flen; i ++ ) { + calculateInverses() { - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + this.boneInverses.length = 0; - } + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - } else { + const inverse = new Matrix4(); - // Bottom faces + if ( this.bones[ i ] ) { - for ( let i = 0; i < flen; i ++ ) { + inverse.copy( this.bones[ i ].matrixWorld ).invert(); - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + } - } + this.boneInverses.push( inverse ); - // Top faces + } - for ( let i = 0; i < flen; i ++ ) { + } - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + pose() { - } + // recover the bind-time world matrices - } + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); + const bone = this.bones[ i ]; + + if ( bone ) { + + bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); } - // Create faces for the z-sides of the shape + } - function buildSideFaces() { + // compute the local matrices, positions, rotations and scales - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + const bone = this.bones[ i ]; - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + if ( bone ) { - //, true - layeroffset += ahole.length; + if ( bone.parent && bone.parent.isBone ) { - } + bone.matrix.copy( bone.parent.matrixWorld ).invert(); + bone.matrix.multiply( bone.matrixWorld ); + } else { - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); + bone.matrix.copy( bone.matrixWorld ); + + } + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } - function sidewalls( contour, layeroffset ) { + } - let i = contour.length; + } - while ( -- i >= 0 ) { + update() { - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; + const bones = this.bones; + const boneInverses = this.boneInverses; + const boneMatrices = this.boneMatrices; + const boneTexture = this.boneTexture; - //console.log('b', i,j, i-1, k,vertices.length); + // flatten bone matrices to array - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { + for ( let i = 0, il = bones.length; i < il; i ++ ) { - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); + // compute the offset between the current and the original transform - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - f4( a, b, c, d ); + _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); + _offsetMatrix.toArray( boneMatrices, i * 16 ); - } + } - } + if ( boneTexture !== null ) { - } + boneTexture.needsUpdate = true; - function v( x, y, z ) { + } - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); + } - } + clone() { + return new Skeleton( this.bones, this.boneInverses ); - function f3( a, b, c ) { + } - addVertex( a ); - addVertex( b ); - addVertex( c ); + computeBoneTexture() { - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); + let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = ceilPowerOfTwo( size ); + size = Math.max( size, 4 ); - } + const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel + boneMatrices.set( this.boneMatrices ); // copy current values - function f4( a, b, c, d ) { + const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + boneTexture.needsUpdate = true; - addVertex( a ); - addVertex( b ); - addVertex( d ); + this.boneMatrices = boneMatrices; + this.boneTexture = boneTexture; + this.boneTextureSize = size; - addVertex( b ); - addVertex( c ); - addVertex( d ); + return this; + } - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + getBoneByName( name ) { - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); + const bone = this.bones[ i ]; + + if ( bone.name === name ) { + + return bone; } - function addVertex( index ) { + } - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); + return undefined; - } + } + dispose( ) { - function addUV( vector2 ) { + if ( this.boneTexture !== null ) { - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); + this.boneTexture.dispose(); - } + this.boneTexture = null; } } - toJSON() { + fromJSON( json, bones ) { - const data = BufferGeometry.prototype.toJSON.call( this ); + this.uuid = json.uuid; - const shapes = this.parameters.shapes; - const options = this.parameters.options; + for ( let i = 0, l = json.bones.length; i < l; i ++ ) { - return toJSON$1( shapes, options, data ); + const uuid = json.bones[ i ]; + let bone = bones[ uuid ]; - } + if ( bone === undefined ) { - } + console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); + bone = new Bone(); - const WorldUVGenerator = { + } - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { + this.bones.push( bone ); + this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; + } - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; + this.init(); - }, + return this; - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { + } - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; + toJSON() { + + const data = { + metadata: { + version: 4.5, + type: 'Skeleton', + generator: 'Skeleton.toJSON' + }, + bones: [], + boneInverses: [] + }; - if ( Math.abs( a_y - b_y ) < 0.01 ) { + data.uuid = this.uuid; - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; + const bones = this.bones; + const boneInverses = this.boneInverses; - } else { + for ( let i = 0, l = bones.length; i < l; i ++ ) { - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; + const bone = bones[ i ]; + data.bones.push( bone.uuid ); + + const boneInverse = boneInverses[ i ]; + data.boneInverses.push( boneInverse.toArray() ); } + return data; + } - }; + } - function toJSON$1( shapes, options, data ) { + class InstancedBufferAttribute extends BufferAttribute { - data.shapes = []; + constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { - if ( Array.isArray( shapes ) ) { + if ( typeof normalized === 'number' ) { - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + meshPerAttribute = normalized; - const shape = shapes[ i ]; + normalized = false; - data.shapes.push( shape.uuid ); + console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); } - } else { + super( array, itemSize, normalized ); - data.shapes.push( shapes.uuid ); + this.meshPerAttribute = meshPerAttribute; } - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); + copy( source ) { - return data; + super.copy( source ); - } + this.meshPerAttribute = source.meshPerAttribute; - class ShapeGeometry extends BufferGeometry { + return this; - constructor( shapes, curveSegments = 12 ) { + } - super(); - this.type = 'ShapeGeometry'; + toJSON() { - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; + const data = super.toJSON(); - // buffers + data.meshPerAttribute = this.meshPerAttribute; - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + data.isInstancedBufferAttribute = true; - // helper variables + return data; - let groupStart = 0; - let groupCount = 0; + } - // allow single and array values for "shapes" parameter + } - if ( Array.isArray( shapes ) === false ) { + InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; - addShape( shapes ); + const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); + const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); - } else { + const _instanceIntersects = []; - for ( let i = 0; i < shapes.length; i ++ ) { + const _mesh = /*@__PURE__*/ new Mesh(); - addShape( shapes[ i ] ); + class InstancedMesh extends Mesh { - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support + constructor( geometry, material, count ) { - groupStart += groupCount; - groupCount = 0; + super( geometry, material ); - } + this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); + this.instanceColor = null; - } + this.count = count; - // build geometry + this.frustumCulled = false; - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + } + copy( source ) { - // helper functions + super.copy( source ); - function addShape( shape ) { + this.instanceMatrix.copy( source.instanceMatrix ); - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); + if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); - let shapeVertices = points.shape; - const shapeHoles = points.holes; + this.count = source.count; - // check direction of vertices + return this; - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { + } - shapeVertices = shapeVertices.reverse(); + getColorAt( index, color ) { - } + color.fromArray( this.instanceColor.array, index * 3 ); - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + } - const shapeHole = shapeHoles[ i ]; + getMatrixAt( index, matrix ) { - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + matrix.fromArray( this.instanceMatrix.array, index * 16 ); - shapeHoles[ i ] = shapeHole.reverse(); + } - } - - } - - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - - // join vertices of inner and outer paths to a single array + raycast( raycaster, intersects ) { - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + const matrixWorld = this.matrixWorld; + const raycastTimes = this.count; - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); + _mesh.geometry = this.geometry; + _mesh.material = this.material; - } + if ( _mesh.material === undefined ) return; - // vertices, normals, uvs + for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { + // calculate the world matrix for each instance - const vertex = shapeVertices[ i ]; + this.getMatrixAt( instanceId, _instanceLocalMatrix ); - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs + _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - } + // the mesh represents this single instance - // incides + _mesh.matrixWorld = _instanceWorldMatrix; - for ( let i = 0, l = faces.length; i < l; i ++ ) { + _mesh.raycast( raycaster, _instanceIntersects ); - const face = faces[ i ]; + // process the result of raycast - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; + for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - indices.push( a, b, c ); - groupCount += 3; + const intersect = _instanceIntersects[ i ]; + intersect.instanceId = instanceId; + intersect.object = this; + intersects.push( intersect ); } + _instanceIntersects.length = 0; + } } - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; + setColorAt( index, color ) { - return toJSON( shapes, data ); + if ( this.instanceColor === null ) { - } + this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); - } + } - function toJSON( shapes, data ) { + color.toArray( this.instanceColor.array, index * 3 ); - data.shapes = []; + } - if ( Array.isArray( shapes ) ) { + setMatrixAt( index, matrix ) { - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + matrix.toArray( this.instanceMatrix.array, index * 16 ); - const shape = shapes[ i ]; + } - data.shapes.push( shape.uuid ); + updateMorphTargets() { - } + } - } else { + dispose() { - data.shapes.push( shapes.uuid ); + this.dispatchEvent( { type: 'dispose' } ); } - return data; - } - /** - * parameters = { - * color: - * } - */ + InstancedMesh.prototype.isInstancedMesh = true; - class ShadowMaterial extends Material { + class LineBasicMaterial extends Material { constructor( parameters ) { super(); - this.type = 'ShadowMaterial'; + this.type = 'LineBasicMaterial'; - this.color = new Color( 0x000000 ); - this.transparent = true; + this.color = new Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; this.setValues( parameters ); } + copy( source ) { super.copy( source ); this.color.copy( source.color ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + return this; } } - ShadowMaterial.prototype.isShadowMaterial = true; + LineBasicMaterial.prototype.isLineBasicMaterial = true; - class RawShaderMaterial extends ShaderMaterial { + const _start$1 = /*@__PURE__*/ new Vector3(); + const _end$1 = /*@__PURE__*/ new Vector3(); + const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); + const _ray$1 = /*@__PURE__*/ new Ray(); + const _sphere$1 = /*@__PURE__*/ new Sphere(); - constructor( parameters ) { + class Line extends Object3D { - super( parameters ); + constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { - this.type = 'RawShaderMaterial'; + super(); - } + this.type = 'Line'; - } + this.geometry = geometry; + this.material = material; - RawShaderMaterial.prototype.isRawShaderMaterial = true; + this.updateMorphTargets(); - /** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ + } - class MeshStandardMaterial extends Material { + copy( source ) { - constructor( parameters ) { + super.copy( source ); - super(); + this.material = source.material; + this.geometry = source.geometry; - this.defines = { 'STANDARD': '' }; + return this; - this.type = 'MeshStandardMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; + computeLineDistances() { - this.map = null; + const geometry = this.geometry; - this.lightMap = null; - this.lightMapIntensity = 1.0; + if ( geometry.isBufferGeometry ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + // we assume non-indexed geometry - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + if ( geometry.index === null ) { - this.bumpMap = null; - this.bumpScale = 1; + const positionAttribute = geometry.attributes.position; + const lineDistances = [ 0 ]; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + _start$1.fromBufferAttribute( positionAttribute, i - 1 ); + _end$1.fromBufferAttribute( positionAttribute, i ); - this.roughnessMap = null; + lineDistances[ i ] = lineDistances[ i - 1 ]; + lineDistances[ i ] += _start$1.distanceTo( _end$1 ); - this.metalnessMap = null; + } - this.alphaMap = null; + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - this.envMap = null; - this.envMapIntensity = 1.0; + } else { - this.refractionRatio = 0.98; + console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } else if ( geometry.isGeometry ) { - this.flatShading = false; + console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.vertexTangents = false; + } - this.setValues( parameters ); + return this; } - copy( source ) { + raycast( raycaster, intersects ) { - super.copy( source ); + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Line.threshold; + const drawRange = geometry.drawRange; - this.defines = { 'STANDARD': '' }; + // Checking boundingSphere distance to ray - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - this.map = source.map; + _sphere$1.copy( geometry.boundingSphere ); + _sphere$1.applyMatrix4( matrixWorld ); + _sphere$1.radius += threshold; - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + // - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + _inverseMatrix$1.copy( matrixWorld ).invert(); + _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + const vStart = new Vector3(); + const vEnd = new Vector3(); + const interSegment = new Vector3(); + const interRay = new Vector3(); + const step = this.isLineSegments ? 2 : 1; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + if ( geometry.isBufferGeometry ) { - this.roughnessMap = source.roughnessMap; + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - this.metalnessMap = source.metalnessMap; + if ( index !== null ) { - this.alphaMap = source.alphaMap; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; + for ( let i = start, l = end - 1; i < l; i += step ) { - this.refractionRatio = source.refractionRatio; + const a = index.getX( i ); + const b = index.getX( i + 1 ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + vStart.fromBufferAttribute( positionAttribute, a ); + vEnd.fromBufferAttribute( positionAttribute, b ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - this.flatShading = source.flatShading; + if ( distSq > localThresholdSq ) continue; - this.vertexTangents = source.vertexTangents; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - return this; + const distance = raycaster.ray.origin.distanceTo( interRay ); - } + if ( distance < raycaster.near || distance > raycaster.far ) continue; - } + intersects.push( { - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - /** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * ior: , - * - * sheen: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ) - * } - */ + } ); - class MeshPhysicalMaterial extends MeshStandardMaterial { + } - constructor( parameters ) { + } else { - super(); + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - this.defines = { + for ( let i = start, l = end - 1; i < l; i += step ) { - 'STANDARD': '', - 'PHYSICAL': '' + vStart.fromBufferAttribute( positionAttribute, i ); + vEnd.fromBufferAttribute( positionAttribute, i + 1 ); - }; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - this.type = 'MeshPhysicalMaterial'; + if ( distSq > localThresholdSq ) continue; - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - this.reflectivity = 0.5; // maps to F0 = 0.04 + const distance = raycaster.ray.origin.distanceTo( interRay ); - Object.defineProperty( this, 'ior', { - get: function () { + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { - return ( 1 + 0.4 * this.reflectivity ) / ( 1 - 0.4 * this.reflectivity ); + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - }, - set: function ( ior ) { + } ); - this.reflectivity = clamp( 2.5 * ( ior - 1 ) / ( ior + 1 ), 0, 1 ); + } } - } ); - this.sheen = null; // null will disable sheen bsdf + } else if ( geometry.isGeometry ) { - this.transmission = 0.0; - this.transmissionMap = null; + console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.setValues( parameters ); + } } - copy( source ) { - - super.copy( source ); + updateMorphTargets() { - this.defines = { + const geometry = this.geometry; - 'STANDARD': '', - 'PHYSICAL': '' + if ( geometry.isBufferGeometry ) { - }; + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); + if ( keys.length > 0 ) { - this.reflectivity = source.reflectivity; + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - if ( source.sheen ) { + if ( morphAttribute !== undefined ) { - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - } else { + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - this.sheen = null; + const name = morphAttribute[ m ].name || String( m ); - } + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; + } - return this; + } - } + } - } + } else { - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; + const morphTargets = geometry.morphTargets; - /** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - class MeshPhongMaterial extends Material { + console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - constructor( parameters ) { + } - super(); + } - this.type = 'MeshPhongMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; + } - this.map = null; + Line.prototype.isLine = true; - this.lightMap = null; - this.lightMapIntensity = 1.0; + const _start = /*@__PURE__*/ new Vector3(); + const _end = /*@__PURE__*/ new Vector3(); - this.aoMap = null; - this.aoMapIntensity = 1.0; + class LineSegments extends Line { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + constructor( geometry, material ) { - this.bumpMap = null; - this.bumpScale = 1; + super( geometry, material ); - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + this.type = 'LineSegments'; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.specularMap = null; + computeLineDistances() { - this.alphaMap = null; + const geometry = this.geometry; - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + if ( geometry.isBufferGeometry ) { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + // we assume non-indexed geometry - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( geometry.index === null ) { - this.flatShading = false; + const positionAttribute = geometry.attributes.position; + const lineDistances = []; - this.setValues( parameters ); + for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - } + _start.fromBufferAttribute( positionAttribute, i ); + _end.fromBufferAttribute( positionAttribute, i + 1 ); - copy( source ) { + lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; + lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); - super.copy( source ); + } - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - this.map = source.map; + } else { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + } - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + } else if ( geometry.isGeometry ) { - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + } - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + return this; - this.specularMap = source.specularMap; + } - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + LineSegments.prototype.isLineSegments = true; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + class LineLoop extends Line { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + constructor( geometry, material ) { - this.flatShading = source.flatShading; + super( geometry, material ); - return this; + this.type = 'LineLoop'; } } - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; + LineLoop.prototype.isLineLoop = true; - /** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - - class MeshToonMaterial extends Material { + class PointsMaterial extends Material { constructor( parameters ) { super(); - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; + this.type = 'PointsMaterial'; this.color = new Color( 0xffffff ); this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; this.alphaMap = null; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + this.size = 1; + this.sizeAttenuation = true; this.setValues( parameters ); @@ -31108,39 +31204,11 @@ this.color.copy( source.color ); this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; return this; @@ -31148,4137 +31216,4082 @@ } - MeshToonMaterial.prototype.isMeshToonMaterial = true; + PointsMaterial.prototype.isPointsMaterial = true; - /** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ + const _inverseMatrix = /*@__PURE__*/ new Matrix4(); + const _ray = /*@__PURE__*/ new Ray(); + const _sphere = /*@__PURE__*/ new Sphere(); + const _position$2 = /*@__PURE__*/ new Vector3(); - class MeshNormalMaterial extends Material { + class Points extends Object3D { - constructor( parameters ) { + constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; + this.type = 'Points'; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + this.geometry = geometry; + this.material = material; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + this.updateMorphTargets(); - this.wireframe = false; - this.wireframeLinewidth = 1; + } - this.fog = false; + copy( source ) { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + super.copy( source ); - this.flatShading = false; + this.material = source.material; + this.geometry = source.geometry; - this.setValues( parameters ); + return this; } - copy( source ) { + raycast( raycaster, intersects ) { - super.copy( source ); + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Points.threshold; + const drawRange = geometry.drawRange; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + // Checking boundingSphere distance to ray - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + _sphere.copy( geometry.boundingSphere ); + _sphere.applyMatrix4( matrixWorld ); + _sphere.radius += threshold; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + // - this.flatShading = source.flatShading; + _inverseMatrix.copy( matrixWorld ).invert(); + _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - return this; + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - } + if ( geometry.isBufferGeometry ) { - } + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + if ( index !== null ) { - /** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - class MeshLambertMaterial extends Material { + for ( let i = start, il = end; i < il; i ++ ) { - constructor( parameters ) { + const a = index.getX( i ); - super(); + _position$2.fromBufferAttribute( positionAttribute, a ); - this.type = 'MeshLambertMaterial'; + testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - this.color = new Color( 0xffffff ); // diffuse + } - this.map = null; + } else { - this.lightMap = null; - this.lightMapIntensity = 1.0; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - this.aoMap = null; - this.aoMapIntensity = 1.0; + for ( let i = start, l = end; i < l; i ++ ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + _position$2.fromBufferAttribute( positionAttribute, i ); - this.specularMap = null; + testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - this.alphaMap = null; + } - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + } - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } else { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.setValues( parameters ); + } } - copy( source ) { + updateMorphTargets() { - super.copy( source ); + const geometry = this.geometry; - this.color.copy( source.color ); + if ( geometry.isBufferGeometry ) { - this.map = source.map; + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( keys.length > 0 ) { - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + if ( morphAttribute !== undefined ) { - this.specularMap = source.specularMap; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - this.alphaMap = source.alphaMap; + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + const name = morphAttribute[ m ].name || String( m ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + } - return this; + } - } + } - } + } else { - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; + const morphTargets = geometry.morphTargets; - /** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * - * flatShading: - * } - */ + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - class MeshMatcapMaterial extends Material { + console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - constructor( parameters ) { + } - super(); + } - this.defines = { 'MATCAP': '' }; + } - this.type = 'MeshMatcapMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse + Points.prototype.isPoints = true; - this.matcap = null; + function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - this.map = null; + const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - this.bumpMap = null; - this.bumpScale = 1; + if ( rayPointDistanceSq < localThresholdSq ) { - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + const intersectPoint = new Vector3(); - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + _ray.closestPointToPoint( point, intersectPoint ); + intersectPoint.applyMatrix4( matrixWorld ); - this.alphaMap = null; + const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( distance < raycaster.near || distance > raycaster.far ) return; - this.flatShading = false; + intersects.push( { - this.setValues( parameters ); + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint, + index: index, + face: null, + object: object + + } ); } + } - copy( source ) { + class VideoTexture extends Texture { - super.copy( source ); + constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - this.defines = { 'MATCAP': '' }; + super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - this.color.copy( source.color ); + this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.matcap = source.matcap; + this.generateMipmaps = false; - this.map = source.map; + const scope = this; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + function updateVideo() { - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + scope.needsUpdate = true; + video.requestVideoFrameCallback( updateVideo ); - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + } - this.alphaMap = source.alphaMap; + if ( 'requestVideoFrameCallback' in video ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + video.requestVideoFrameCallback( updateVideo ); - this.flatShading = source.flatShading; + } - return this; + } + + clone() { + + return new this.constructor( this.image ).copy( this ); } - } + update() { - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; + const video = this.image; + const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - /** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ + if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - class LineDashedMaterial extends LineBasicMaterial { + this.needsUpdate = true; - constructor( parameters ) { + } - super(); + } - this.type = 'LineDashedMaterial'; + } - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + VideoTexture.prototype.isVideoTexture = true; - this.setValues( parameters ); + class FramebufferTexture extends Texture { - } + constructor( width, height, format ) { - copy( source ) { + super( { width, height } ); - super.copy( source ); + this.format = format; - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - return this; + this.generateMipmaps = false; + + this.needsUpdate = true; } } - LineDashedMaterial.prototype.isLineDashedMaterial = true; + FramebufferTexture.prototype.isFramebufferTexture = true; - const AnimationUtils = { + class CompressedTexture extends Texture { - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { + constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - if ( AnimationUtils.isTypedArray( array ) ) { + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - } + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - return array.slice( from, to ); + this.flipY = false; - }, + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { + this.generateMipmaps = false; - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + } - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + } - return new type( array ); // create typed array + CompressedTexture.prototype.isCompressedTexture = true; - } + class CanvasTexture extends Texture { - return Array.prototype.slice.call( array ); // create Array + constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - }, + super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - isTypedArray: function ( object ) { + this.needsUpdate = true; - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + } - }, + } - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { + CanvasTexture.prototype.isCanvasTexture = true; - function compareTime( i, j ) { + /** + * Extensible curve object. + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following curves inherit from THREE.Curve: + * + * -- 2D curves -- + * THREE.ArcCurve + * THREE.CubicBezierCurve + * THREE.EllipseCurve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.SplineCurve + * + * -- 3D curves -- + * THREE.CatmullRomCurve3 + * THREE.CubicBezierCurve3 + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * + * A series of curves can be represented as a THREE.CurvePath. + * + **/ - return times[ i ] - times[ j ]; + class Curve { - } + constructor() { - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; + this.type = 'Curve'; - result.sort( compareTime ); + this.arcLengthDivisions = 200; - return result; + } - }, + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { + getPoint( /* t, optionalTarget */ ) { - const nValues = values.length; - const result = new values.constructor( nValues ); + console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + return null; - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + } - const srcOffset = order[ i ] * stride; + // Get point at relative position in curve according to arc length + // - u [0 .. 1] - for ( let j = 0; j !== stride; ++ j ) { + getPointAt( u, optionalTarget ) { - result[ dstOffset ++ ] = values[ srcOffset + j ]; + const t = this.getUtoTmapping( u ); + return this.getPoint( t, optionalTarget ); - } + } - } + // Get sequence of points using getPoint( t ) - return result; + getPoints( divisions = 5 ) { - }, + const points = []; - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { + for ( let d = 0; d <= divisions; d ++ ) { - let i = 1, key = jsonKeys[ 0 ]; + points.push( this.getPoint( d / divisions ) ); - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + } - key = jsonKeys[ i ++ ]; - - } - - if ( key === undefined ) return; // no data - - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data - - if ( Array.isArray( value ) ) { + return points; - do { + } - value = key[ valuePropertyName ]; + // Get sequence of points using getPointAt( u ) - if ( value !== undefined ) { + getSpacedPoints( divisions = 5 ) { - times.push( key.time ); - values.push.apply( values, value ); // push all elements + const points = []; - } + for ( let d = 0; d <= divisions; d ++ ) { - key = jsonKeys[ i ++ ]; + points.push( this.getPointAt( d / divisions ) ); - } while ( key !== undefined ); + } - } else if ( value.toArray !== undefined ) { + return points; - // ...assume THREE.Math-ish + } - do { + // Get total curve arc length - value = key[ valuePropertyName ]; + getLength() { - if ( value !== undefined ) { + const lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - times.push( key.time ); - value.toArray( values, values.length ); + } - } + // Get list of cumulative segment lengths - key = jsonKeys[ i ++ ]; + getLengths( divisions = this.arcLengthDivisions ) { - } while ( key !== undefined ); + if ( this.cacheArcLengths && + ( this.cacheArcLengths.length === divisions + 1 ) && + ! this.needsUpdate ) { - } else { + return this.cacheArcLengths; - // otherwise push as-is + } - do { + this.needsUpdate = false; - value = key[ valuePropertyName ]; + const cache = []; + let current, last = this.getPoint( 0 ); + let sum = 0; - if ( value !== undefined ) { + cache.push( 0 ); - times.push( key.time ); - values.push( value ); + for ( let p = 1; p <= divisions; p ++ ) { - } + current = this.getPoint( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; - key = jsonKeys[ i ++ ]; + } - } while ( key !== undefined ); + this.cacheArcLengths = cache; - } + return cache; // { sums: cache, sum: sum }; Sum is in the last element. - }, + } - subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { + updateArcLengths() { - const clip = sourceClip.clone(); + this.needsUpdate = true; + this.getLengths(); - clip.name = name; + } - const tracks = []; + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - for ( let i = 0; i < clip.tracks.length; ++ i ) { + getUtoTmapping( u, distance ) { - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); + const arcLengths = this.getLengths(); - const times = []; - const values = []; + let i = 0; + const il = arcLengths.length; - for ( let j = 0; j < track.times.length; ++ j ) { + let targetArcLength; // The targeted u distance value to get - const frame = track.times[ j ] * fps; + if ( distance ) { - if ( frame < startFrame || frame >= endFrame ) continue; + targetArcLength = distance; - times.push( track.times[ j ] ); + } else { - for ( let k = 0; k < valueSize; ++ k ) { + targetArcLength = u * arcLengths[ il - 1 ]; - values.push( track.values[ j * valueSize + k ] ); + } - } + // binary search for the index with largest value smaller than target u distance - } + let low = 0, high = il - 1, comparison; - if ( times.length === 0 ) continue; + while ( low <= high ) { - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - tracks.push( track ); + comparison = arcLengths[ i ] - targetArcLength; - } + if ( comparison < 0 ) { - clip.tracks = tracks; + low = i + 1; - // find minimum .times value across all tracks in the trimmed clip + } else if ( comparison > 0 ) { - let minStartTime = Infinity; + high = i - 1; - for ( let i = 0; i < clip.tracks.length; ++ i ) { + } else { - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { + high = i; + break; - minStartTime = clip.tracks[ i ].times[ 0 ]; + // DONE } } - // shift all tracks such that clip begins at t=0 + i = high; - for ( let i = 0; i < clip.tracks.length; ++ i ) { + if ( arcLengths[ i ] === targetArcLength ) { - clip.tracks[ i ].shift( - 1 * minStartTime ); + return i / ( il - 1 ); } - clip.resetDuration(); + // we could get finer grain at lengths, or use simple interpolation between two points - return clip; + const lengthBefore = arcLengths[ i ]; + const lengthAfter = arcLengths[ i + 1 ]; - }, + const segmentLength = lengthAfter - lengthBefore; - makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { + // determine where we are between the 'before' and 'after' points - if ( fps <= 0 ) fps = 30; + const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - const numTracks = referenceClip.tracks.length; - const referenceTime = referenceFrame / fps; + // add that fractional amount to t - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { + const t = ( i + segmentFraction ) / ( il - 1 ); - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; + return t; - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; + } - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; + getTangent( t, optionalTarget ) { - } ); + const delta = 0.0001; + let t1 = t - delta; + let t2 = t + delta; - if ( targetTrack === undefined ) continue; + // Capping in case of danger - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + const pt1 = this.getPoint( t1 ); + const pt2 = this.getPoint( t2 ); - referenceOffset = referenceValueSize / 3; + const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - } + tangent.copy( pt2 ).sub( pt1 ).normalize(); - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); + return tangent; - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + } - targetOffset = targetValueSize / 3; + getTangentAt( u, optionalTarget ) { - } + const t = this.getUtoTmapping( u ); + return this.getTangent( t, optionalTarget ); - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; + } - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { + computeFrenetFrames( segments, closed ) { - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { + const normal = new Vector3(); - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + const tangents = []; + const normals = []; + const binormals = []; - } else { + const vec = new Vector3(); + const mat = new Matrix4(); - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); + // compute the tangent vectors for each segment on the curve - } + for ( let i = 0; i <= segments; i ++ ) { - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { + const u = i / segments; - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); + tangents[ i ] = this.getTangentAt( u, new Vector3() ); - } + } - // Subtract the reference value from all of the track values + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + let min = Number.MAX_VALUE; + const tx = Math.abs( tangents[ 0 ].x ); + const ty = Math.abs( tangents[ 0 ].y ); + const tz = Math.abs( tangents[ 0 ].z ); - const valueStart = j * targetValueSize + targetOffset; + if ( tx <= min ) { - if ( referenceTrackType === 'quaternion' ) { + min = tx; + normal.set( 1, 0, 0 ); - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); + } - } else { + if ( ty <= min ) { - const valueEnd = targetValueSize - targetOffset * 2; + min = ty; + normal.set( 0, 1, 0 ); - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { + } - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; + if ( tz <= min ) { - } + normal.set( 0, 0, 1 ); - } + } - } + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - } + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - targetClip.blendMode = AdditiveAnimationBlendMode; - return targetClip; + // compute the slowly-varying normal and binormal vectors for each segment on the curve - } + for ( let i = 1; i <= segments; i ++ ) { - }; + normals[ i ] = normals[ i - 1 ].clone(); - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ + binormals[ i ] = binormals[ i - 1 ].clone(); - class Interpolant { + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + if ( vec.length() > Number.EPSILON ) { - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; + vec.normalize(); - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - this.settings = null; - this.DefaultSettings_ = {}; + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - } + } - evaluate( t ) { + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + } - validate_interval: { + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - seek: { + if ( closed === true ) { - let right; + let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); + theta /= segments; - linear_scan: { + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + theta = - theta; - for ( let giveUpAt = i1 + 2; ; ) { + } - if ( t1 === undefined ) { + for ( let i = 1; i <= segments; i ++ ) { - if ( t < t0 ) break forward_scan; + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - // after end + } - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + } - } + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; - if ( i1 === giveUpAt ) break; // this loop + } - t0 = t1; - t1 = pp[ ++ i1 ]; + clone() { - if ( t < t1 ) { + return new this.constructor().copy( this ); - // we have arrived at the sought interval - break seek; + } - } + copy( source ) { - } + this.arcLengthDivisions = source.arcLengthDivisions; - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + return this; - } + } - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + toJSON() { - // looping? + const data = { + metadata: { + version: 4.5, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; - const t1global = pp[ 1 ]; + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; - if ( t < t1global ) { + return data; - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + } - } + fromJSON( json ) { - // linear reverse scan + this.arcLengthDivisions = json.arcLengthDivisions; - for ( let giveUpAt = i1 - 2; ; ) { + return this; - if ( t0 === undefined ) { + } - // before start + } - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + class EllipseCurve extends Curve { - } + constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { - if ( i1 === giveUpAt ) break; // this loop + super(); - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + this.type = 'EllipseCurve'; - if ( t >= t0 ) { + this.aX = aX; + this.aY = aY; - // we have arrived at the sought interval - break seek; + this.xRadius = xRadius; + this.yRadius = yRadius; - } + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - } + this.aClockwise = aClockwise; - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + this.aRotation = aRotation; - } + } - // the interval is valid + getPoint( t, optionalTarget ) { - break validate_interval; + const point = optionalTarget || new Vector2(); - } // linear scan + const twoPi = Math.PI * 2; + let deltaAngle = this.aEndAngle - this.aStartAngle; + const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - // binary search + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - while ( i1 < right ) { + if ( deltaAngle < Number.EPSILON ) { - const mid = ( i1 + right ) >>> 1; + if ( samePoints ) { - if ( t < pp[ mid ] ) { + deltaAngle = 0; - right = mid; + } else { - } else { + deltaAngle = twoPi; - i1 = mid + 1; + } - } + } - } + if ( this.aClockwise === true && ! samePoints ) { - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + if ( deltaAngle === twoPi ) { - // check boundary cases, again + deltaAngle = - twoPi; - if ( t0 === undefined ) { + } else { - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + deltaAngle = deltaAngle - twoPi; - } + } - if ( t1 === undefined ) { + } - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + const angle = this.aStartAngle + t * deltaAngle; + let x = this.aX + this.xRadius * Math.cos( angle ); + let y = this.aY + this.yRadius * Math.sin( angle ); - } + if ( this.aRotation !== 0 ) { - } // seek + const cos = Math.cos( this.aRotation ); + const sin = Math.sin( this.aRotation ); - this._cachedIndex = i1; + const tx = x - this.aX; + const ty = y - this.aY; - this.intervalChanged_( i1, t0, t1 ); + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; - } // validate_interval + } - return this.interpolate_( i1, t0, t, t1 ); + return point.set( x, y ); } - getSettings_() { - - return this.settings || this.DefaultSettings_; - - } + copy( source ) { - copySampleValue_( index ) { + super.copy( source ); - // copies a sample value to the result buffer + this.aX = source.aX; + this.aY = source.aY; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; - for ( let i = 0; i !== stride; ++ i ) { + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; - result[ i ] = values[ offset + i ]; + this.aClockwise = source.aClockwise; - } + this.aRotation = source.aRotation; - return result; + return this; } - // Template methods for derived classes: - - interpolate_( /* i1, t0, t, t1 */ ) { + toJSON() { - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer + const data = super.toJSON(); - } + data.aX = this.aX; + data.aY = this.aY; - intervalChanged_( /* i1, t0, t1 */ ) { + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; - // empty + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; - } + data.aClockwise = this.aClockwise; - } + data.aRotation = this.aRotation; - // ALIAS DEFINITIONS + return data; - Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; - Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; + } - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ + fromJSON( json ) { - class CubicInterpolant extends Interpolant { + super.fromJSON( json ); - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + this.aX = json.aX; + this.aY = json.aY; - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; - this.DefaultSettings_ = { + this.aClockwise = json.aClockwise; - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + this.aRotation = json.aRotation; - }; + return this; } - intervalChanged_( i1, t0, t1 ) { - - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, - - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; + } - if ( tPrev === undefined ) { + EllipseCurve.prototype.isEllipseCurve = true; - switch ( this.getSettings_().endingStart ) { + class ArcCurve extends EllipseCurve { - case ZeroSlopeEnding: + constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - break; + this.type = 'ArcCurve'; - case WrapAroundEnding: + } - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + } - break; + ArcCurve.prototype.isArcCurve = true; - default: // ZeroCurvatureEnding + /** + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; - } + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM - } + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ - if ( tNext === undefined ) { + function CubicPoly() { - switch ( this.getSettings_().endingEnd ) { + let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - case ZeroSlopeEnding: + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + function init( x0, x1, t0, t1 ) { - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; + c0 = x0; + c1 = t0; + c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; - break; + } - case WrapAroundEnding: + return { - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + initCatmullRom: function ( x0, x1, x2, x3, tension ) { - break; + init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - default: // ZeroCurvatureEnding + }, - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - } + // compute tangents when parameterized in [t1,t2] + let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - } + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; + init( x1, x2, t1, t2 ); - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; + }, - } + calc: function ( t ) { - interpolate_( i1, t0, t, t1 ) { + const t2 = t * t; + const t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, + }; - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; + } - // evaluate polynomials + // - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; + const tmp = new Vector3(); + const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - // combine data linearly + class CatmullRomCurve3 extends Curve { - for ( let i = 0; i !== stride; ++ i ) { + constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + super(); - } + this.type = 'CatmullRomCurve3'; - return result; + this.points = points; + this.closed = closed; + this.curveType = curveType; + this.tension = tension; } - } + getPoint( t, optionalTarget = new Vector3() ) { - class LinearInterpolant extends Interpolant { + const point = optionalTarget; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + const points = this.points; + const l = points.length; - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + const p = ( l - ( this.closed ? 0 : 1 ) ) * t; + let intPoint = Math.floor( p ); + let weight = p - intPoint; - } + if ( this.closed ) { - interpolate_( i1, t0, t, t1 ) { + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } else if ( weight === 0 && intPoint === l - 1 ) { - offset1 = i1 * stride, - offset0 = offset1 - stride, + intPoint = l - 2; + weight = 1; - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + } - for ( let i = 0; i !== stride; ++ i ) { + let p0, p3; // 4 points (p1 & p2 defined below) - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + if ( this.closed || intPoint > 0 ) { - } + p0 = points[ ( intPoint - 1 ) % l ]; - return result; + } else { - } + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; - } + } - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ + const p1 = points[ intPoint % l ]; + const p2 = points[ ( intPoint + 1 ) % l ]; - class DiscreteInterpolant extends Interpolant { + if ( this.closed || intPoint + 2 < l ) { - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + p3 = points[ ( intPoint + 2 ) % l ]; - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + } else { - } + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; - interpolate_( i1 /*, t0, t, t1 */ ) { + } - return this.copySampleValue_( i1 - 1 ); + if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - } + // init Centripetal / Chordal Catmull-Rom + const pow = this.curveType === 'chordal' ? 0.5 : 0.25; + let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - } + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; - class KeyframeTrack { + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - constructor( name, times, values, interpolation ) { + } else if ( this.curveType === 'catmullrom' ) { - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - this.name = name; + } - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); + point.set( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - this.setInterpolation( interpolation || this.DefaultInterpolation ); + return point; } - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + copy( source ) { - static toJSON( track ) { + super.copy( source ); - const trackType = track.constructor; + this.points = []; - let json; + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - // derived classes can define a static toJSON method - if ( trackType.toJSON !== this.toJSON ) { + const point = source.points[ i ]; - json = trackType.toJSON( track ); + this.points.push( point.clone() ); - } else { + } - // by default, we assume the data can be serialized as-is - json = { + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) + return this; - }; + } - const interpolation = track.getInterpolation(); + toJSON() { - if ( interpolation !== track.DefaultInterpolation ) { + const data = super.toJSON(); - json.interpolation = interpolation; + data.points = []; - } + for ( let i = 0, l = this.points.length; i < l; i ++ ) { + + const point = this.points[ i ]; + data.points.push( point.toArray() ); } - json.type = track.ValueTypeName; // mandatory + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; - return json; + return data; } - InterpolantFactoryMethodDiscrete( result ) { + fromJSON( json ) { - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); + super.fromJSON( json ); - } + this.points = []; - InterpolantFactoryMethodLinear( result ) { + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); + const point = json.points[ i ]; + this.points.push( new Vector3().fromArray( point ) ); - } + } - InterpolantFactoryMethodSmooth( result ) { + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); + return this; } - setInterpolation( interpolation ) { + } - let factoryMethod; + CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - switch ( interpolation ) { + /** + * Bezier Curves formulas obtained from + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + */ - case InterpolateDiscrete: + function CatmullRom( t, p0, p1, p2, p3 ) { - factoryMethod = this.InterpolantFactoryMethodDiscrete; + const v0 = ( p2 - p0 ) * 0.5; + const v1 = ( p3 - p1 ) * 0.5; + const t2 = t * t; + const t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - break; + } - case InterpolateLinear: + // - factoryMethod = this.InterpolantFactoryMethodLinear; + function QuadraticBezierP0( t, p ) { - break; + const k = 1 - t; + return k * k * p; - case InterpolateSmooth: + } - factoryMethod = this.InterpolantFactoryMethodSmooth; + function QuadraticBezierP1( t, p ) { - break; + return 2 * ( 1 - t ) * t * p; - } + } - if ( factoryMethod === undefined ) { + function QuadraticBezierP2( t, p ) { - const message = 'unsupported interpolation for ' + - this.ValueTypeName + ' keyframe track named ' + this.name; + return t * t * p; - if ( this.createInterpolant === undefined ) { + } - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { + function QuadraticBezier( t, p0, p1, p2 ) { - this.setInterpolation( this.DefaultInterpolation ); + return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + + QuadraticBezierP2( t, p2 ); - } else { + } - throw new Error( message ); // fatal, in this case + // - } + function CubicBezierP0( t, p ) { - } + const k = 1 - t; + return k * k * k * p; - console.warn( 'THREE.KeyframeTrack:', message ); - return this; + } - } + function CubicBezierP1( t, p ) { - this.createInterpolant = factoryMethod; + const k = 1 - t; + return 3 * k * k * t * p; - return this; + } - } + function CubicBezierP2( t, p ) { - getInterpolation() { + return 3 * ( 1 - t ) * t * t * p; - switch ( this.createInterpolant ) { + } - case this.InterpolantFactoryMethodDiscrete: + function CubicBezierP3( t, p ) { - return InterpolateDiscrete; + return t * t * t * p; - case this.InterpolantFactoryMethodLinear: + } - return InterpolateLinear; + function CubicBezier( t, p0, p1, p2, p3 ) { - case this.InterpolantFactoryMethodSmooth: + return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + + CubicBezierP3( t, p3 ); - return InterpolateSmooth; + } - } + class CubicBezierCurve extends Curve { - } + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - getValueSize() { + super(); - return this.values.length / this.times.length; + this.type = 'CubicBezierCurve'; + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; } - // move all keyframes either forwards or backwards in time - shift( timeOffset ) { + getPoint( t, optionalTarget = new Vector2() ) { - if ( timeOffset !== 0.0 ) { + const point = optionalTarget; - const times = this.times; + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - for ( let i = 0, n = times.length; i !== n; ++ i ) { + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) + ); - times[ i ] += timeOffset; + return point; - } + } - } + copy( source ) { + + super.copy( source ); + + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); return this; } - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale( timeScale ) { + toJSON() { - if ( timeScale !== 1.0 ) { + const data = super.toJSON(); - const times = this.times; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - for ( let i = 0, n = times.length; i !== n; ++ i ) { + return data; - times[ i ] *= timeScale; + } - } + fromJSON( json ) { - } + super.fromJSON( json ); + + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); return this; } - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim( startTime, endTime ) { - - const times = this.times, - nKeys = times.length; - - let from = 0, - to = nKeys - 1; - - while ( from !== nKeys && times[ from ] < startTime ) { - - ++ from; + } - } + CubicBezierCurve.prototype.isCubicBezierCurve = true; - while ( to !== - 1 && times[ to ] > endTime ) { + class CubicBezierCurve3 extends Curve { - -- to; + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { - } + super(); - ++ to; // inclusive -> exclusive bound + this.type = 'CubicBezierCurve3'; - if ( from !== 0 || to !== nKeys ) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { + } - to = Math.max( to, 1 ); - from = to - 1; + getPoint( t, optionalTarget = new Vector3() ) { - } + const point = optionalTarget; - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - } + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), + CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) + ); - return this; + return point; } - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate() { - - let valid = true; + copy( source ) { - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + super.copy( source ); - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - } + return this; - const times = this.times, - values = this.values, + } - nKeys = times.length; + toJSON() { - if ( nKeys === 0 ) { + const data = super.toJSON(); - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - } + return data; - let prevTime = null; + } - for ( let i = 0; i !== nKeys; i ++ ) { + fromJSON( json ) { - const currTime = times[ i ]; + super.fromJSON( json ); - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; + return this; - } + } - if ( prevTime !== null && prevTime > currTime ) { + } - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; + CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - } + class LineCurve extends Curve { - prevTime = currTime; + constructor( v1 = new Vector2(), v2 = new Vector2() ) { - } + super(); - if ( values !== undefined ) { + this.type = 'LineCurve'; - if ( AnimationUtils.isTypedArray( values ) ) { + this.v1 = v1; + this.v2 = v2; - for ( let i = 0, n = values.length; i !== n; ++ i ) { + } - const value = values[ i ]; + getPoint( t, optionalTarget = new Vector2() ) { - if ( isNaN( value ) ) { + const point = optionalTarget; - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; + if ( t === 1 ) { - } + point.copy( this.v2 ); - } + } else { - } + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); } - return valid; + return point; } - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize() { - - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + return this.getPoint( u, optionalTarget ); - lastIndex = times.length - 1; + } - let writeIndex = 1; + getTangent( t, optionalTarget ) { - for ( let i = 1; i < lastIndex; ++ i ) { + const tangent = optionalTarget || new Vector2(); - let keep = false; + tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - const time = times[ i ]; - const timeNext = times[ i + 1 ]; + return tangent; - // remove adjacent keyframes scheduled at the same time + } - if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { + copy( source ) { - if ( ! smoothInterpolation ) { + super.copy( source ); - // remove unnecessary keyframes same as their neighbors + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + return this; - for ( let j = 0; j !== stride; ++ j ) { + } - const value = values[ offset + j ]; + toJSON() { - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + const data = super.toJSON(); - keep = true; - break; + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - } + } - } else { + fromJSON( json ) { - keep = true; + super.fromJSON( json ); - } + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - } + return this; - // in-place compaction + } - if ( keep ) { + } - if ( i !== writeIndex ) { + LineCurve.prototype.isLineCurve = true; - times[ writeIndex ] = times[ i ]; + class LineCurve3 extends Curve { - const readOffset = i * stride, - writeOffset = writeIndex * stride; + constructor( v1 = new Vector3(), v2 = new Vector3() ) { - for ( let j = 0; j !== stride; ++ j ) { + super(); - values[ writeOffset + j ] = values[ readOffset + j ]; + this.type = 'LineCurve3'; + this.isLineCurve3 = true; - } + this.v1 = v1; + this.v2 = v2; - } + } + getPoint( t, optionalTarget = new Vector3() ) { - ++ writeIndex; + const point = optionalTarget; - } + if ( t === 1 ) { - } + point.copy( this.v2 ); - // flush last keyframe (compaction looks ahead) + } else { - if ( lastIndex > 0 ) { + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - times[ writeIndex ] = times[ lastIndex ]; + } - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { + return point; - values[ writeOffset + j ] = values[ readOffset + j ]; + } + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - } + return this.getPoint( u, optionalTarget ); - ++ writeIndex; + } + copy( source ) { - } + super.copy( source ); - if ( writeIndex !== times.length ) { + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + return this; - } else { + } + toJSON() { - this.times = times; - this.values = values; + const data = super.toJSON(); - } + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - return this; + return data; } + fromJSON( json ) { - clone() { - - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); - - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); + super.fromJSON( json ); - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - return track; + return this; } } - KeyframeTrack.prototype.TimeBufferType = Float32Array; - KeyframeTrack.prototype.ValueBufferType = Float32Array; - KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + class QuadraticBezierCurve extends Curve { - /** - * A Track of Boolean keyframe values. - */ - class BooleanKeyframeTrack extends KeyframeTrack {} + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { - BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; - BooleanKeyframeTrack.prototype.ValueBufferType = Array; - BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; - BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; - BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + super(); - /** - * A Track of keyframe values that represent color. - */ - class ColorKeyframeTrack extends KeyframeTrack {} + this.type = 'QuadraticBezierCurve'; - ColorKeyframeTrack.prototype.ValueTypeName = 'color'; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - /** - * A Track of numeric keyframe values. - */ - class NumberKeyframeTrack extends KeyframeTrack {} + } - NumberKeyframeTrack.prototype.ValueTypeName = 'number'; + getPoint( t, optionalTarget = new Vector2() ) { - /** - * Spherical linear unit quaternion interpolant. - */ + const point = optionalTarget; - class QuaternionLinearInterpolant extends Interpolant { + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ) + ); - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + return point; } - interpolate_( i1, t0, t, t1 ) { + copy( source ) { - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + super.copy( source ); - alpha = ( t - t0 ) / ( t1 - t0 ); + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - let offset = i1 * stride; + return this; - for ( let end = offset + stride; offset !== end; offset += 4 ) { + } - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); + toJSON() { - } + const data = super.toJSON(); - return result; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + + return data; } - } + fromJSON( json ) { - /** - * A Track of quaternion keyframe values. - */ - class QuaternionKeyframeTrack extends KeyframeTrack { + super.fromJSON( json ); - InterpolantFactoryMethodLinear( result ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); + return this; } } - QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; - // ValueBufferType is inherited - QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - /** - * A Track that interpolates Strings - */ - class StringKeyframeTrack extends KeyframeTrack {} + class QuadraticBezierCurve3 extends Curve { - StringKeyframeTrack.prototype.ValueTypeName = 'string'; - StringKeyframeTrack.prototype.ValueBufferType = Array; - StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; - StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; - StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { - /** - * A Track of vectored keyframe values. - */ - class VectorKeyframeTrack extends KeyframeTrack {} + super(); - VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; + this.type = 'QuadraticBezierCurve3'; - class AnimationClip { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { + } - this.name = name; - this.tracks = tracks; - this.duration = duration; - this.blendMode = blendMode; + getPoint( t, optionalTarget = new Vector3() ) { - this.uuid = generateUUID(); + const point = optionalTarget; - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - this.resetDuration(); + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ), + QuadraticBezier( t, v0.z, v1.z, v2.z ) + ); - } + return point; } + copy( source ) { - static parse( json ) { + super.copy( source ); - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { + return this; - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); + } - } + toJSON() { - const clip = new this( json.name, json.duration, tracks, json.blendMode ); - clip.uuid = json.uuid; + const data = super.toJSON(); - return clip; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + + return data; } - static toJSON( clip ) { + fromJSON( json ) { - const tracks = [], - clipTracks = clip.tracks; + super.fromJSON( json ); - const json = { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode + return this; - }; + } - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { + } - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - } + class SplineCurve extends Curve { - return json; + constructor( points = [] ) { + + super(); + + this.type = 'SplineCurve'; + + this.points = points; } - static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { + getPoint( t, optionalTarget = new Vector2() ) { - const numMorphTargets = morphTargetSequence.length; - const tracks = []; + const point = optionalTarget; - for ( let i = 0; i < numMorphTargets; i ++ ) { + const points = this.points; + const p = ( points.length - 1 ) * t; - let times = []; - let values = []; + const intPoint = Math.floor( p ); + const weight = p - intPoint; - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + const p1 = points[ intPoint ]; + const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - values.push( 0, 1, 0 ); + point.set( + CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), + CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) + ); - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); + return point; - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { + } - times.push( numMorphTargets ); - values.push( values[ 0 ] ); + copy( source ) { - } + super.copy( source ); - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - - } + this.points = []; - return new this( name, - 1, tracks ); + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - } + const point = source.points[ i ]; - static findByName( objectOrClipArray, name ) { + this.points.push( point.clone() ); - let clipArray = objectOrClipArray; + } - if ( ! Array.isArray( objectOrClipArray ) ) { + return this; - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + } - } + toJSON() { - for ( let i = 0; i < clipArray.length; i ++ ) { + const data = super.toJSON(); - if ( clipArray[ i ].name === name ) { + data.points = []; - return clipArray[ i ]; + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - } + const point = this.points[ i ]; + data.points.push( point.toArray() ); } - return null; + return data; } - static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { + fromJSON( json ) { - const animationToMorphTargets = {}; + super.fromJSON( json ); - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; + this.points = []; - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); + const point = json.points[ i ]; + this.points.push( new Vector2().fromArray( point ) ); - if ( parts && parts.length > 1 ) { + } - const name = parts[ 1 ]; + return this; - let animationMorphTargets = animationToMorphTargets[ name ]; + } - if ( ! animationMorphTargets ) { + } - animationToMorphTargets[ name ] = animationMorphTargets = []; + SplineCurve.prototype.isSplineCurve = true; - } + var Curves = /*#__PURE__*/Object.freeze({ + __proto__: null, + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve + }); - animationMorphTargets.push( morphTarget ); + /************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ - } + class CurvePath extends Curve { - } + constructor() { - const clips = []; + super(); - for ( const name in animationToMorphTargets ) { + this.type = 'CurvePath'; - clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + this.curves = []; + this.autoClose = false; // Automatically closes the path - } + } - return clips; + add( curve ) { + + this.curves.push( curve ); } - // parse the animation.hierarchy format - static parseAnimation( animation, bones ) { + closePath() { - if ( ! animation ) { + // Add a line curve if start and end of lines are not connected + const startPoint = this.curves[ 0 ].getPoint( 0 ); + const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; + if ( ! startPoint.equals( endPoint ) ) { + + this.curves.push( new LineCurve( endPoint, startPoint ) ); } - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { + } - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: - const times = []; - const values = []; + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); + getPoint( t, optionalTarget ) { - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + const d = t * this.getLength(); + const curveLengths = this.getCurveLengths(); + let i = 0; - destTracks.push( new trackType( trackName, times, values ) ); + // To think about boundaries points. - } + while ( i < curveLengths.length ) { - } + if ( curveLengths[ i ] >= d ) { - }; + const diff = curveLengths[ i ] - d; + const curve = this.curves[ i ]; - const tracks = []; + const segmentLength = curve.getLength(); + const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; + return curve.getPointAt( u, optionalTarget ); - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; + } - const hierarchyTracks = animation.hierarchy || []; + i ++; - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { + } - const animationKeys = hierarchyTracks[ h ].keys; + return null; - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; + // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { + + points.push( points[ 0 ] ); } - return this; + return points; } - validate() { + copy( source ) { - let valid = true; + super.copy( source ); - for ( let i = 0; i < this.tracks.length; i ++ ) { + this.curves = []; - valid = valid && this.tracks[ i ].validate(); + for ( let i = 0, l = source.curves.length; i < l; i ++ ) { + + const curve = source.curves[ i ]; + + this.curves.push( curve.clone() ); } - return valid; + this.autoClose = source.autoClose; + + return this; } - optimize() { + toJSON() { - for ( let i = 0; i < this.tracks.length; i ++ ) { + const data = super.toJSON(); - this.tracks[ i ].optimize(); + data.autoClose = this.autoClose; + data.curves = []; + + for ( let i = 0, l = this.curves.length; i < l; i ++ ) { + + const curve = this.curves[ i ]; + data.curves.push( curve.toJSON() ); } - return this; + return data; } - clone() { + fromJSON( json ) { - const tracks = []; + super.fromJSON( json ); - for ( let i = 0; i < this.tracks.length; i ++ ) { + this.autoClose = json.autoClose; + this.curves = []; - tracks.push( this.tracks[ i ].clone() ); + for ( let i = 0, l = json.curves.length; i < l; i ++ ) { + + const curve = json.curves[ i ]; + this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } - return new this.constructor( this.name, this.duration, tracks, this.blendMode ); + return this; } - toJSON() { + } - return this.constructor.toJSON( this ); + class Path extends CurvePath { - } + constructor( points ) { - } + super(); + this.type = 'Path'; - function getTrackTypeForValueTypeName( typeName ) { + this.currentPoint = new Vector2(); - switch ( typeName.toLowerCase() ) { + if ( points ) { - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': + this.setFromPoints( points ); - return NumberKeyframeTrack; + } - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': + } - return VectorKeyframeTrack; + setFromPoints( points ) { - case 'color': + this.moveTo( points[ 0 ].x, points[ 0 ].y ); - return ColorKeyframeTrack; + for ( let i = 1, l = points.length; i < l; i ++ ) { - case 'quaternion': + this.lineTo( points[ i ].x, points[ i ].y ); - return QuaternionKeyframeTrack; + } - case 'bool': - case 'boolean': + return this; - return BooleanKeyframeTrack; + } - case 'string': + moveTo( x, y ) { - return StringKeyframeTrack; + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - } + return this; - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); + } - } + lineTo( x, y ) { - function parseKeyframeTrack( json ) { + const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); + this.curves.push( curve ); - if ( json.type === undefined ) { + this.currentPoint.set( x, y ); - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); + return this; } - const trackType = getTrackTypeForValueTypeName( json.type ); + quadraticCurveTo( aCPx, aCPy, aX, aY ) { - if ( json.times === undefined ) { + const curve = new QuadraticBezierCurve( + this.currentPoint.clone(), + new Vector2( aCPx, aCPy ), + new Vector2( aX, aY ) + ); - const times = [], values = []; + this.curves.push( curve ); - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + this.currentPoint.set( aX, aY ); - json.times = times; - json.values = values; + return this; } - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - return trackType.parse( json ); + const curve = new CubicBezierCurve( + this.currentPoint.clone(), + new Vector2( aCP1x, aCP1y ), + new Vector2( aCP2x, aCP2y ), + new Vector2( aX, aY ) + ); - } else { + this.curves.push( curve ); - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + this.currentPoint.set( aX, aY ); + + return this; } - } + splineThru( pts /*Array of Vector*/ ) { - const Cache = { + const npts = [ this.currentPoint.clone() ].concat( pts ); - enabled: false, + const curve = new SplineCurve( npts ); + this.curves.push( curve ); - files: {}, + this.currentPoint.copy( pts[ pts.length - 1 ] ); - add: function ( key, file ) { + return this; - if ( this.enabled === false ) return; + } - // console.log( 'THREE.Cache', 'Adding key:', key ); + arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - this.files[ key ] = file; + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - }, + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - get: function ( key ) { + return this; - if ( this.enabled === false ) return; + } - // console.log( 'THREE.Cache', 'Checking key:', key ); + absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - return this.files[ key ]; + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - }, + return this; - remove: function ( key ) { + } - delete this.files[ key ]; + ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - }, + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - clear: function () { + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - this.files = {}; + return this; } - }; + absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - class LoadingManager { + const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - constructor( onLoad, onProgress, onError ) { + if ( this.curves.length > 0 ) { - const scope = this; + // if a previous curve is present, attempt to join + const firstPoint = curve.getPoint( 0 ); - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; + if ( ! firstPoint.equals( this.currentPoint ) ) { - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor + this.lineTo( firstPoint.x, firstPoint.y ); - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + } - this.itemStart = function ( url ) { + } - itemsTotal ++; + this.curves.push( curve ); - if ( isLoading === false ) { + const lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); - if ( scope.onStart !== undefined ) { + return this; - scope.onStart( url, itemsLoaded, itemsTotal ); + } - } + copy( source ) { - } + super.copy( source ); - isLoading = true; + this.currentPoint.copy( source.currentPoint ); - }; + return this; - this.itemEnd = function ( url ) { + } - itemsLoaded ++; + toJSON() { - if ( scope.onProgress !== undefined ) { + const data = super.toJSON(); - scope.onProgress( url, itemsLoaded, itemsTotal ); + data.currentPoint = this.currentPoint.toArray(); - } + return data; - if ( itemsLoaded === itemsTotal ) { + } - isLoading = false; + fromJSON( json ) { - if ( scope.onLoad !== undefined ) { + super.fromJSON( json ); - scope.onLoad(); + this.currentPoint.fromArray( json.currentPoint ); - } + return this; - } + } - }; + } - this.itemError = function ( url ) { + new Vector3(); + new Vector3(); + new Vector3(); + new Triangle(); - if ( scope.onError !== undefined ) { + class Shape extends Path { - scope.onError( url ); + constructor( points ) { - } + super( points ); - }; + this.uuid = generateUUID(); - this.resolveURL = function ( url ) { + this.type = 'Shape'; - if ( urlModifier ) { + this.holes = []; - return urlModifier( url ); + } - } + getPointsHoles( divisions ) { - return url; + const holesPts = []; - }; + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - this.setURLModifier = function ( transform ) { + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - urlModifier = transform; + } - return this; + return holesPts; - }; + } - this.addHandler = function ( regex, loader ) { + // get points of shape and holes (keypoints based on segments parameter) - handlers.push( regex, loader ); + extractPoints( divisions ) { - return this; + return { + + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) }; - this.removeHandler = function ( regex ) { + } - const index = handlers.indexOf( regex ); + copy( source ) { - if ( index !== - 1 ) { + super.copy( source ); - handlers.splice( index, 2 ); + this.holes = []; - } + for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - return this; + const hole = source.holes[ i ]; - }; + this.holes.push( hole.clone() ); - this.getHandler = function ( file ) { + } - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { + return this; - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; + } - if ( regex.global ) regex.lastIndex = 0; // see #17920 + toJSON() { - if ( regex.test( file ) ) { + const data = super.toJSON(); - return loader; + data.uuid = this.uuid; + data.holes = []; - } + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - } + const hole = this.holes[ i ]; + data.holes.push( hole.toJSON() ); - return null; + } - }; + return data; } - } - - const DefaultLoadingManager = new LoadingManager(); + fromJSON( json ) { - class Loader { + super.fromJSON( json ); - constructor( manager ) { + this.uuid = json.uuid; + this.holes = []; - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + for ( let i = 0, l = json.holes.length; i < l; i ++ ) { - this.crossOrigin = 'anonymous'; - this.withCredentials = false; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; + const hole = json.holes[ i ]; + this.holes.push( new Path().fromJSON( hole ) ); - } + } - load( /* url, onLoad, onProgress, onError */ ) {} + return this; - loadAsync( url, onProgress ) { + } - const scope = this; + } - return new Promise( function ( resolve, reject ) { + /** + * Port from https://github.com/mapbox/earcut (v2.2.2) + */ - scope.load( url, resolve, onProgress, reject ); + const Earcut = { - } ); + triangulate: function ( data, holeIndices, dim = 2 ) { - } + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; + let outerNode = linkedList( data, 0, outerLen, dim, true ); + const triangles = []; - parse( /* data */ ) {} + if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - setCrossOrigin( crossOrigin ) { + let minX, minY, maxX, maxY, x, y, invSize; - this.crossOrigin = crossOrigin; - return this; + if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); - } + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if ( data.length > 80 * dim ) { - setWithCredentials( value ) { + minX = maxX = data[ 0 ]; + minY = maxY = data[ 1 ]; - this.withCredentials = value; - return this; + for ( let i = dim; i < outerLen; i += dim ) { - } + x = data[ i ]; + y = data[ i + 1 ]; + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; - setPath( path ) { + } - this.path = path; - return this; + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max( maxX - minX, maxY - minY ); + invSize = invSize !== 0 ? 1 / invSize : 0; - } + } - setResourcePath( resourcePath ) { + earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - this.resourcePath = resourcePath; - return this; + return triangles; } - setRequestHeader( requestHeader ) { - - this.requestHeader = requestHeader; - return this; + }; - } + // create a circular doubly linked list from polygon points in the specified winding order + function linkedList( data, start, end, dim, clockwise ) { - } + let i, last; - const loading = {}; + if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - class FileLoader extends Loader { + for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - constructor( manager ) { + } else { - super( manager ); + for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); } - load( url, onLoad, onProgress, onError ) { + if ( last && equals( last, last.next ) ) { - if ( url === undefined ) url = ''; + removeNode( last ); + last = last.next; - if ( this.path !== undefined ) url = this.path + url; + } - url = this.manager.resolveURL( url ); + return last; - const scope = this; + } - const cached = Cache.get( url ); + // eliminate colinear or duplicate points + function filterPoints( start, end ) { - if ( cached !== undefined ) { + if ( ! start ) return start; + if ( ! end ) end = start; - scope.manager.itemStart( url ); + let p = start, + again; + do { - setTimeout( function () { + again = false; - if ( onLoad ) onLoad( cached ); + if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - scope.manager.itemEnd( url ); + removeNode( p ); + p = end = p.prev; + if ( p === p.next ) break; + again = true; - }, 0 ); + } else { - return cached; + p = p.next; } - // Check if request is duplicate - - if ( loading[ url ] !== undefined ) { - - loading[ url ].push( { + } while ( again || p !== end ); - onLoad: onLoad, - onProgress: onProgress, - onError: onError + return end; - } ); + } - return; + // main ear slicing loop which triangulates a polygon (given as a linked list) + function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - } + if ( ! ear ) return; - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; + // interlink polygon nodes in z-order + if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { + let stop = ear, + prev, next; - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; + // iterate through ears, slicing them one by one + while ( ear.prev !== ear.next ) { - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); + prev = ear.prev; + next = ear.next; - if ( isBase64 ) data = atob( data ); + if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - try { + // cut off the triangle + triangles.push( prev.i / dim ); + triangles.push( ear.i / dim ); + triangles.push( next.i / dim ); - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); + removeNode( ear ); - switch ( responseType ) { + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; - case 'arraybuffer': - case 'blob': + continue; - const view = new Uint8Array( data.length ); + } - for ( let i = 0; i < data.length; i ++ ) { + ear = next; - view[ i ] = data.charCodeAt( i ); + // if we looped through the whole remaining polygon and can't find any more ears + if ( ear === stop ) { - } + // try filtering points and slicing again + if ( ! pass ) { - if ( responseType === 'blob' ) { + earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - response = new Blob( [ view.buffer ], { type: mimeType } ); + // if this didn't work, try curing all small self-intersections locally - } else { + } else if ( pass === 1 ) { - response = view.buffer; + ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); + earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - } + // as a last resort, try splitting the remaining polygon into two - break; + } else if ( pass === 2 ) { - case 'document': + splitEarcut( ear, triangles, dim, minX, minY, invSize ); - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); + } - break; + break; - case 'json': + } - response = JSON.parse( data ); + } - break; + } - default: // 'text' or other + // check whether a polygon node forms a valid ear with adjacent nodes + function isEar( ear ) { - response = data; + const a = ear.prev, + b = ear, + c = ear.next; - break; + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - } + // now make sure we don't have other points inside the potential ear + let p = ear.next.next; - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + while ( p !== ear.prev ) { - if ( onLoad ) onLoad( response ); + if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.next; - scope.manager.itemEnd( url ); + } - }, 0 ); + return true; - } catch ( error ) { + } - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + function isEarHashed( ear, minX, minY, invSize ) { - if ( onError ) onError( error ); + const a = ear.prev, + b = ear, + c = ear.next; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - }, 0 ); + // triangle bbox; min & max are calculated like this for speed + const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), + minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), + maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), + maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - } + // z-order range for the current triangle bbox; + const minZ = zOrder( minTX, minTY, minX, minY, invSize ), + maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - } else { + let p = ear.prevZ, + n = ear.nextZ; - // Initialise array for duplicate requests + // look for points inside the triangle in both directions + while ( p && p.z >= minZ && n && n.z <= maxZ ) { - loading[ url ] = []; + if ( p !== ear.prev && p !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - loading[ url ].push( { + if ( n !== ear.prev && n !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; - onLoad: onLoad, - onProgress: onProgress, - onError: onError + } - } ); + // look for remaining points in decreasing z-order + while ( p && p.z >= minZ ) { - request = new XMLHttpRequest(); + if ( p !== ear.prev && p !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - request.open( 'GET', url, true ); + } - request.addEventListener( 'load', function ( event ) { + // look for remaining points in increasing z-order + while ( n && n.z <= maxZ ) { - const response = this.response; + if ( n !== ear.prev && n !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; - const callbacks = loading[ url ]; + } - delete loading[ url ]; + return true; - if ( this.status === 200 || this.status === 0 ) { + } - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. + // go through all polygon nodes and cure small local self-intersections + function cureLocalIntersections( start, triangles, dim ) { - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); + let p = start; + do { - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); + const a = p.prev, + b = p.next.next; - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); + triangles.push( a.i / dim ); + triangles.push( p.i / dim ); + triangles.push( b.i / dim ); - } + // remove two nodes involved + removeNode( p ); + removeNode( p.next ); - scope.manager.itemEnd( url ); + p = start = b; - } else { + } - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + p = p.next; - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + } while ( p !== start ); - } + return filterPoints( p ); - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - } + // try splitting polygon into two and triangulate them independently + function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - }, false ); + // look for a valid diagonal that divides the polygon into two + let a = start; + do { - request.addEventListener( 'progress', function ( event ) { + let b = a.next.next; + while ( b !== a.prev ) { - const callbacks = loading[ url ]; + if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + // split the polygon in two by the diagonal + let c = splitPolygon( a, b ); - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); + // filter colinear points around the cuts + a = filterPoints( a, a.next ); + c = filterPoints( c, c.next ); - } + // run earcut on each half + earcutLinked( a, triangles, dim, minX, minY, invSize ); + earcutLinked( c, triangles, dim, minX, minY, invSize ); + return; - }, false ); + } - request.addEventListener( 'error', function ( event ) { + b = b.next; - const callbacks = loading[ url ]; + } - delete loading[ url ]; + a = a.next; - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + } while ( a !== start ); - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + } - } + // link every hole into the outer loop, producing a single-ring polygon without holes + function eliminateHoles( data, holeIndices, outerNode, dim ) { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + const queue = []; + let i, len, start, end, list; - }, false ); + for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - request.addEventListener( 'abort', function ( event ) { + start = holeIndices[ i ] * dim; + end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; + list = linkedList( data, start, end, dim, false ); + if ( list === list.next ) list.steiner = true; + queue.push( getLeftmost( list ) ); - const callbacks = loading[ url ]; + } - delete loading[ url ]; + queue.sort( compareX ); - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + // process holes from left to right + for ( i = 0; i < queue.length; i ++ ) { - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + eliminateHole( queue[ i ], outerNode ); + outerNode = filterPoints( outerNode, outerNode.next ); - } + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + return outerNode; - }, false ); + } - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + function compareX( a, b ) { - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); + return a.x - b.x; - for ( const header in this.requestHeader ) { + } - request.setRequestHeader( header, this.requestHeader[ header ] ); + // find a bridge between vertices that connects hole with an outer ring and link it + function eliminateHole( hole, outerNode ) { - } + outerNode = findHoleBridge( hole, outerNode ); + if ( outerNode ) { - request.send( null ); + const b = splitPolygon( outerNode, hole ); - } + // filter collinear points around the cuts + filterPoints( outerNode, outerNode.next ); + filterPoints( b, b.next ); - scope.manager.itemStart( url ); + } - return request; + } - } + // David Eberly's algorithm for finding a bridge between hole and outer polygon + function findHoleBridge( hole, outerNode ) { - setResponseType( value ) { + let p = outerNode; + const hx = hole.x; + const hy = hole.y; + let qx = - Infinity, m; - this.responseType = value; - return this; + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + do { - } + if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - setMimeType( value ) { + const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); + if ( x <= hx && x > qx ) { - this.mimeType = value; - return this; + qx = x; + if ( x === hx ) { - } + if ( hy === p.y ) return p; + if ( hy === p.next.y ) return p.next; - } + } - class ImageLoader extends Loader { + m = p.x < p.next.x ? p : p.next; - constructor( manager ) { + } - super( manager ); + } - } + p = p.next; - load( url, onLoad, onProgress, onError ) { + } while ( p !== outerNode ); - if ( this.path !== undefined ) url = this.path + url; + if ( ! m ) return null; - url = this.manager.resolveURL( url ); + if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - const scope = this; + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point - const cached = Cache.get( url ); + const stop = m, + mx = m.x, + my = m.y; + let tanMin = Infinity, tan; - if ( cached !== undefined ) { + p = m; - scope.manager.itemStart( url ); + do { - setTimeout( function () { + if ( hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { - if ( onLoad ) onLoad( cached ); + tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential - scope.manager.itemEnd( url ); + if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - }, 0 ); + m = p; + tanMin = tan; - return cached; + } } - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + p = p.next; - function onImageLoad() { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); - - Cache.add( url, this ); - - if ( onLoad ) onLoad( this ); - - scope.manager.itemEnd( url ); - - } - - function onImageError( event ) { - - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + } while ( p !== stop ); - if ( onError ) onError( event ); + return m; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - } + // whether sector in vertex m contains sector in vertex p in the same coordinates + function sectorContainsSector( m, p ) { - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); + return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - if ( url.substr( 0, 5 ) !== 'data:' ) { + } - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + // interlink polygon nodes in z-order + function indexCurve( start, minX, minY, invSize ) { - } + let p = start; + do { - scope.manager.itemStart( url ); + if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; - image.src = url; + } while ( p !== start ); - return image; + p.prevZ.nextZ = null; + p.prevZ = null; - } + sortLinked( p ); } - class CubeTextureLoader extends Loader { - - constructor( manager ) { + // Simon Tatham's linked list merge sort algorithm + // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + function sortLinked( list ) { - super( manager ); + let i, p, q, e, tail, numMerges, pSize, qSize, + inSize = 1; - } + do { - load( urls, onLoad, onProgress, onError ) { + p = list; + list = null; + tail = null; + numMerges = 0; - const texture = new CubeTexture(); + while ( p ) { - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + numMerges ++; + q = p; + pSize = 0; + for ( i = 0; i < inSize; i ++ ) { - let loaded = 0; + pSize ++; + q = q.nextZ; + if ( ! q ) break; - function loadTexture( i ) { + } - loader.load( urls[ i ], function ( image ) { + qSize = inSize; - texture.images[ i ] = image; + while ( pSize > 0 || ( qSize > 0 && q ) ) { - loaded ++; + if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - if ( loaded === 6 ) { + e = p; + p = p.nextZ; + pSize --; - texture.needsUpdate = true; + } else { - if ( onLoad ) onLoad( texture ); + e = q; + q = q.nextZ; + qSize --; } - }, undefined, onError ); + if ( tail ) tail.nextZ = e; + else list = e; - } + e.prevZ = tail; + tail = e; - for ( let i = 0; i < urls.length; ++ i ) { + } - loadTexture( i ); + p = q; } - return texture; - - } - - } + tail.nextZ = null; + inSize *= 2; - class TextureLoader extends Loader { + } while ( numMerges > 1 ); - constructor( manager ) { + return list; - super( manager ); + } - } + // z-order of a point given coords and inverse of the longer side of data bbox + function zOrder( x, y, minX, minY, invSize ) { - load( url, onLoad, onProgress, onError ) { + // coords are transformed into non-negative 15-bit integer range + x = 32767 * ( x - minX ) * invSize; + y = 32767 * ( y - minY ) * invSize; - const texture = new Texture(); + x = ( x | ( x << 8 ) ) & 0x00FF00FF; + x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; + x = ( x | ( x << 2 ) ) & 0x33333333; + x = ( x | ( x << 1 ) ) & 0x55555555; - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + y = ( y | ( y << 8 ) ) & 0x00FF00FF; + y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; + y = ( y | ( y << 2 ) ) & 0x33333333; + y = ( y | ( y << 1 ) ) & 0x55555555; - loader.load( url, function ( image ) { + return x | ( y << 1 ); - texture.image = image; + } - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; + // find the leftmost node of a polygon ring + function getLeftmost( start ) { - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; + let p = start, + leftmost = start; + do { - if ( onLoad !== undefined ) { + if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; + p = p.next; - onLoad( texture ); + } while ( p !== start ); - } + return leftmost; - }, onProgress, onError ); + } - return texture; + // check if a point lies within a convex triangle + function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - } + return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && + ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && + ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; } - /** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ + // check if a diagonal between two polygon nodes is valid (lies in polygon interior) + function isValidDiagonal( a, b ) { - class Curve { + return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // doesn't intersect other edges + ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible + ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors + equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - constructor() { + } - this.type = 'Curve'; + // signed area of a triangle + function area( p, q, r ) { - this.arcLengthDivisions = 200; + return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - } + } - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] + // check if two points are equal + function equals( p1, p2 ) { - getPoint( /* t, optionalTarget */ ) { + return p1.x === p2.x && p1.y === p2.y; - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; + } - } + // check if two segments intersect + function intersects( p1, q1, p2, q2 ) { - // Get point at relative position in curve according to arc length - // - u [0 .. 1] + const o1 = sign( area( p1, q1, p2 ) ); + const o2 = sign( area( p1, q1, q2 ) ); + const o3 = sign( area( p2, q2, p1 ) ); + const o4 = sign( area( p2, q2, q1 ) ); - getPointAt( u, optionalTarget ) { + if ( o1 !== o2 && o3 !== o4 ) return true; // general case - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); + if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - } + return false; - // Get sequence of points using getPoint( t ) + } - getPoints( divisions = 5 ) { + // for collinear points p, q, r, check if point q lies on segment pr + function onSegment( p, q, r ) { - const points = []; + return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - for ( let d = 0; d <= divisions; d ++ ) { + } - points.push( this.getPoint( d / divisions ) ); + function sign( num ) { - } + return num > 0 ? 1 : num < 0 ? - 1 : 0; - return points; + } - } + // check if a polygon diagonal intersects any polygon segments + function intersectsPolygon( a, b ) { - // Get sequence of points using getPointAt( u ) + let p = a; + do { - getSpacedPoints( divisions = 5 ) { + if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects( p, p.next, a, b ) ) return true; + p = p.next; - const points = []; + } while ( p !== a ); - for ( let d = 0; d <= divisions; d ++ ) { + return false; - points.push( this.getPointAt( d / divisions ) ); + } - } + // check if a polygon diagonal is locally inside the polygon + function locallyInside( a, b ) { - return points; + return area( a.prev, a, a.next ) < 0 ? + area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : + area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - } + } - // Get total curve arc length + // check if the middle point of a polygon diagonal is inside the polygon + function middleInside( a, b ) { - getLength() { + let p = a, + inside = false; + const px = ( a.x + b.x ) / 2, + py = ( a.y + b.y ) / 2; + do { - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && + ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) + inside = ! inside; + p = p.next; - } + } while ( p !== a ); - // Get list of cumulative segment lengths + return inside; - getLengths( divisions = this.arcLengthDivisions ) { + } - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { + // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; + // if one belongs to the outer ring and another to a hole, it merges it into a single ring + function splitPolygon( a, b ) { - return this.cacheArcLengths; + const a2 = new Node( a.i, a.x, a.y ), + b2 = new Node( b.i, b.x, b.y ), + an = a.next, + bp = b.prev; - } + a.next = b; + b.prev = a; - this.needsUpdate = false; + a2.next = an; + an.prev = a2; - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; + b2.next = a2; + a2.prev = b2; - cache.push( 0 ); + bp.next = b2; + b2.prev = bp; - for ( let p = 1; p <= divisions; p ++ ) { + return b2; - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + } - } + // create a node and optionally link it with previous one (in a circular doubly linked list) + function insertNode( i, x, y, last ) { - this.cacheArcLengths = cache; + const p = new Node( i, x, y ); - return cache; // { sums: cache, sum: sum }; Sum is in the last element. + if ( ! last ) { - } + p.prev = p; + p.next = p; - updateArcLengths() { + } else { - this.needsUpdate = true; - this.getLengths(); + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; } - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping( u, distance ) { + return p; - const arcLengths = this.getLengths(); + } - let i = 0; - const il = arcLengths.length; + function removeNode( p ) { - let targetArcLength; // The targeted u distance value to get + p.next.prev = p.prev; + p.prev.next = p.next; - if ( distance ) { + if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; + if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - targetArcLength = distance; + } - } else { + function Node( i, x, y ) { - targetArcLength = u * arcLengths[ il - 1 ]; + // vertex index in coordinates array + this.i = i; - } + // vertex coordinates + this.x = x; + this.y = y; - // binary search for the index with largest value smaller than target u distance + // previous and next vertex nodes in a polygon ring + this.prev = null; + this.next = null; - let low = 0, high = il - 1, comparison; + // z-order curve value + this.z = null; - while ( low <= high ) { + // previous and next nodes in z-order + this.prevZ = null; + this.nextZ = null; - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + // indicates whether this is a steiner point + this.steiner = false; - comparison = arcLengths[ i ] - targetArcLength; + } - if ( comparison < 0 ) { + function signedArea( data, start, end, dim ) { - low = i + 1; + let sum = 0; + for ( let i = start, j = end - dim; i < end; i += dim ) { - } else if ( comparison > 0 ) { + sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); + j = i; - high = i - 1; + } - } else { + return sum; - high = i; - break; + } - // DONE + class ShapeUtils { - } + // calculate area of the contour polygon - } + static area( contour ) { - i = high; + const n = contour.length; + let a = 0.0; - if ( arcLengths[ i ] === targetArcLength ) { + for ( let p = n - 1, q = 0; q < n; p = q ++ ) { - return i / ( il - 1 ); + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } - // we could get finer grain at lengths, or use simple interpolation between two points + return a * 0.5; - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; + } - const segmentLength = lengthAfter - lengthBefore; + static isClockWise( pts ) { - // determine where we are between the 'before' and 'after' points + return ShapeUtils.area( pts ) < 0; - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + } - // add that fractional amount to t + static triangulateShape( contour, holes ) { - const t = ( i + segmentFraction ) / ( il - 1 ); + const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] + const holeIndices = []; // array of hole indices + const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] - return t; + removeDupEndPts( contour ); + addContour( vertices, contour ); - } + // - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation + let holeIndex = contour.length; - getTangent( t, optionalTarget ) { + holes.forEach( removeDupEndPts ); - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; + for ( let i = 0; i < holes.length; i ++ ) { - // Capping in case of danger + holeIndices.push( holeIndex ); + holeIndex += holes[ i ].length; + addContour( vertices, holes[ i ] ); - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + } - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); + // - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); + const triangles = Earcut.triangulate( vertices, holeIndices ); - tangent.copy( pt2 ).sub( pt1 ).normalize(); + // - return tangent; + for ( let i = 0; i < triangles.length; i += 3 ) { - } + faces.push( triangles.slice( i, i + 3 ) ); - getTangentAt( u, optionalTarget ) { + } - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); + return faces; } - computeFrenetFrames( segments, closed ) { - - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - - const normal = new Vector3(); - - const tangents = []; - const normals = []; - const binormals = []; + } - const vec = new Vector3(); - const mat = new Matrix4(); + function removeDupEndPts( points ) { - // compute the tangent vectors for each segment on the curve + const l = points.length; - for ( let i = 0; i <= segments; i ++ ) { + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - const u = i / segments; + points.pop(); - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); + } - } + } - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component + function addContour( vertices, contour ) { - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); + for ( let i = 0; i < contour.length; i ++ ) { - if ( tx <= min ) { + vertices.push( contour[ i ].x ); + vertices.push( contour[ i ].y ); - min = tx; - normal.set( 1, 0, 0 ); + } - } + } - if ( ty <= min ) { + /** + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * depth: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline (including bevelOffset) is bevel + * bevelOffset: , // how far from shape outline does bevel start + * bevelSegments: , // number of bevel layers + * + * extrudePath: // curve to extrude shape along + * + * UVGenerator: // object that provides UV generator functions + * + * } + */ - min = ty; - normal.set( 0, 1, 0 ); + class ExtrudeGeometry extends BufferGeometry { - } + constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { - if ( tz <= min ) { + super(); - normal.set( 0, 0, 1 ); + this.type = 'ExtrudeGeometry'; - } + this.parameters = { + shapes: shapes, + options: options + }; - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + const scope = this; + const verticesArray = []; + const uvArray = []; - // compute the slowly-varying normal and binormal vectors for each segment on the curve + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - for ( let i = 1; i <= segments; i ++ ) { + const shape = shapes[ i ]; + addShape( shape ); - normals[ i ] = normals[ i - 1 ].clone(); + } - binormals[ i ] = binormals[ i - 1 ].clone(); + // build geometry - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); - if ( vec.length() > Number.EPSILON ) { + this.computeVertexNormals(); - vec.normalize(); + // functions - const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + function addShape( shape ) { - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + const placeholder = []; - } + // options - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + const steps = options.steps !== undefined ? options.steps : 1; + let depth = options.depth !== undefined ? options.depth : 1; - } + let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; + let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; + let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; + let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; + let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + const extrudePath = options.extrudePath; - if ( closed === true ) { + const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; + // deprecated options - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { + if ( options.amount !== undefined ) { - theta = - theta; + console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); + depth = options.amount; } - for ( let i = 1; i <= segments; i ++ ) { + // - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + let extrudePts, extrudeByPath = false; + let splineTube, binormal, normal, position2; - } + if ( extrudePath ) { - } + extrudePts = extrudePath.getSpacedPoints( steps ); - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - } + // SETUP TNB variables - clone() { + // TODO1 - have a .isClosed in spline? - return new this.constructor().copy( this ); + splineTube = extrudePath.computeFrenetFrames( steps, false ); - } + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - copy( source ) { + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); - this.arcLengthDivisions = source.arcLengthDivisions; + } - return this; + // Safeguards if bevels are not enabled - } + if ( ! bevelEnabled ) { - toJSON() { + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + bevelOffset = 0; - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' } - }; - - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; - return data; + // Variables initialization - } + const shapePoints = shape.extractPoints( curveSegments ); - fromJSON( json ) { + let vertices = shapePoints.shape; + const holes = shapePoints.holes; - this.arcLengthDivisions = json.arcLengthDivisions; + const reverse = ! ShapeUtils.isClockWise( vertices ); - return this; + if ( reverse ) { - } + vertices = vertices.reverse(); - } + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - class EllipseCurve extends Curve { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { + const ahole = holes[ h ]; - super(); + if ( ShapeUtils.isClockWise( ahole ) ) { - this.type = 'EllipseCurve'; + holes[ h ] = ahole.reverse(); - this.aX = aX; - this.aY = aY; + } - this.xRadius = xRadius; - this.yRadius = yRadius; + } - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; + } - this.aClockwise = aClockwise; - this.aRotation = aRotation; + const faces = ShapeUtils.triangulateShape( vertices, holes ); - } + /* Vertices */ - getPoint( t, optionalTarget ) { + const contour = vertices; // vertices has all points but contour has only points of circumference - const point = optionalTarget || new Vector2(); + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + const ahole = holes[ h ]; - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + vertices = vertices.concat( ahole ); - if ( deltaAngle < Number.EPSILON ) { + } - if ( samePoints ) { - deltaAngle = 0; + function scalePt2( pt, vec, size ) { - } else { + if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); - deltaAngle = twoPi; + return vec.clone().multiplyScalar( size ).add( pt ); } - } + const vlen = vertices.length, flen = faces.length; - if ( this.aClockwise === true && ! samePoints ) { - if ( deltaAngle === twoPi ) { + // Find directions for point movement - deltaAngle = - twoPi; - } else { + function getBevelVec( inPt, inPrev, inNext ) { - deltaAngle = deltaAngle - twoPi; + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. - } + let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - } + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); + const v_prev_x = inPt.x - inPrev.x, + v_prev_y = inPt.y - inPrev.y; + const v_next_x = inNext.x - inPt.x, + v_next_y = inNext.y - inPt.y; - if ( this.aRotation !== 0 ) { + const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); + // check for collinear edges + const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - const tx = x - this.aX; - const ty = y - this.aY; + if ( Math.abs( collinear0 ) > Number.EPSILON ) { - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; + // not collinear - } + // length of vectors for normalizing - return point.set( x, y ); + const v_prev_len = Math.sqrt( v_prev_lensq ); + const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - } + // shift adjacent points by unit vectors to the left - copy( source ) { + const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - super.copy( source ); + const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - this.aX = source.aX; - this.aY = source.aY; + // scaling factor for v_prev to intersection point - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; + const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; + // vector from inPt to intersection point - this.aClockwise = source.aClockwise; + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - this.aRotation = source.aRotation; + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { - return this; + return new Vector2( v_trans_x, v_trans_y ); - } + } else { - toJSON() { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); - const data = super.toJSON(); + } - data.aX = this.aX; - data.aY = this.aY; + } else { - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; + // handle special case of collinear edges - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; + let direction_eq = false; // assumes: opposite - data.aClockwise = this.aClockwise; + if ( v_prev_x > Number.EPSILON ) { - data.aRotation = this.aRotation; + if ( v_next_x > Number.EPSILON ) { - return data; + direction_eq = true; - } + } - fromJSON( json ) { + } else { - super.fromJSON( json ); + if ( v_prev_x < - Number.EPSILON ) { - this.aX = json.aX; - this.aY = json.aY; + if ( v_next_x < - Number.EPSILON ) { - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; + direction_eq = true; - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; + } - this.aClockwise = json.aClockwise; + } else { - this.aRotation = json.aRotation; + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - return this; + direction_eq = true; - } + } - } + } - EllipseCurve.prototype.isEllipseCurve = true; + } - class ArcCurve extends EllipseCurve { + if ( direction_eq ) { - constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); - super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + } else { - this.type = 'ArcCurve'; + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); - } + } - } + } - ArcCurve.prototype.isArcCurve = true; + return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - /** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ + } - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM + const contourMovements = []; - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ + for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - function CubicPoly() { + if ( j === il ) j = 0; + if ( k === il ) k = 0; - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; + } - } + const holesMovements = []; + let oneHoleMovements, verticesMovements = contourMovements.concat(); - return { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - initCatmullRom: function ( x0, x1, x2, x3, tension ) { + const ahole = holes[ h ]; - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + oneHoleMovements = []; - }, + for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { + if ( j === il ) j = 0; + if ( k === il ) k = 0; - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + } - init( x1, x2, t1, t2 ); + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); - }, + } - calc: function ( t ) { - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; + // Loop bevelSegments, 1 for the front, 1 for the back - } + for ( let b = 0; b < bevelSegments; b ++ ) { - }; + //for ( b = bevelSegments; b > 0; b -- ) { - } + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - // + // contract shape - const tmp = new Vector3(); - const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); + for ( let i = 0, il = contour.length; i < il; i ++ ) { - class CatmullRomCurve3 extends Curve { + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { + v( vert.x, vert.y, - z ); - super(); + } - this.type = 'CatmullRomCurve3'; + // expand holes - this.points = points; - this.closed = closed; - this.curveType = curveType; - this.tension = tension; + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - } + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - getPoint( t, optionalTarget = new Vector3() ) { + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - const point = optionalTarget; + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - const points = this.points; - const l = points.length; + v( vert.x, vert.y, - z ); - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; + } - if ( this.closed ) { + } - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; + } - } else if ( weight === 0 && intPoint === l - 1 ) { + const bs = bevelSize + bevelOffset; - intPoint = l - 2; - weight = 1; + // Back facing vertices - } + for ( let i = 0; i < vlen; i ++ ) { - let p0, p3; // 4 points (p1 & p2 defined below) + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - if ( this.closed || intPoint > 0 ) { + if ( ! extrudeByPath ) { - p0 = points[ ( intPoint - 1 ) % l ]; + v( vert.x, vert.y, 0 ); - } else { + } else { - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - } + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - if ( this.closed || intPoint + 2 < l ) { + v( position2.x, position2.y, position2.z ); - p3 = points[ ( intPoint + 2 ) % l ]; + } - } else { + } - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; + // Add stepped vertices... + // Including front facing vertices - } + for ( let s = 1; s <= steps; s ++ ) { - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { + for ( let i = 0; i < vlen; i ++ ) { - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + if ( ! extrudeByPath ) { - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + v( vert.x, vert.y, depth / steps * s ); - } else if ( this.curveType === 'catmullrom' ) { + } else { - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - } + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - return point; + v( position2.x, position2.y, position2.z ); - } + } - copy( source ) { + } - super.copy( source ); + } - this.points = []; - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + // Add bevel segments planes - const point = source.points[ i ]; + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( let b = bevelSegments - 1; b >= 0; b -- ) { - this.points.push( point.clone() ); + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - } + // contract shape - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; + for ( let i = 0, il = contour.length; i < il; i ++ ) { - return this; + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, depth + z ); - } + } - toJSON() { + // expand holes - const data = super.toJSON(); + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - data.points = []; + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - const point = this.points[ i ]; - data.points.push( point.toArray() ); + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } + if ( ! extrudeByPath ) { - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; + v( vert.x, vert.y, depth + z ); - return data; + } else { - } + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - fromJSON( json ) { + } - super.fromJSON( json ); + } - this.points = []; + } - for ( let i = 0, l = json.points.length; i < l; i ++ ) { + } - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); + /* Faces */ - } + // Top and bottom faces - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; + buildLidFaces(); - return this; + // Sides faces - } + buildSideFaces(); - } - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + ///// Internal functions - /** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ + function buildLidFaces() { - function CatmullRom( t, p0, p1, p2, p3 ) { + const start = verticesArray.length / 3; - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + if ( bevelEnabled ) { - } + let layer = 0; // steps + 1 + let offset = vlen * layer; - // + // Bottom faces - function QuadraticBezierP0( t, p ) { + for ( let i = 0; i < flen; i ++ ) { - const k = 1 - t; - return k * k * p; + const face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - } + } - function QuadraticBezierP1( t, p ) { + layer = steps + bevelSegments * 2; + offset = vlen * layer; - return 2 * ( 1 - t ) * t * p; + // Top faces - } + for ( let i = 0; i < flen; i ++ ) { - function QuadraticBezierP2( t, p ) { + const face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - return t * t * p; + } - } + } else { - function QuadraticBezier( t, p0, p1, p2 ) { + // Bottom faces - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); + for ( let i = 0; i < flen; i ++ ) { - } + const face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - // + } - function CubicBezierP0( t, p ) { + // Top faces - const k = 1 - t; - return k * k * k * p; + for ( let i = 0; i < flen; i ++ ) { - } + const face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - function CubicBezierP1( t, p ) { + } - const k = 1 - t; - return 3 * k * k * t * p; + } - } + scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - function CubicBezierP2( t, p ) { + } - return 3 * ( 1 - t ) * t * t * p; + // Create faces for the z-sides of the shape - } + function buildSideFaces() { - function CubicBezierP3( t, p ) { + const start = verticesArray.length / 3; + let layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; - return t * t * t * p; + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - } + const ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); - function CubicBezier( t, p0, p1, p2, p3 ) { + //, true + layeroffset += ahole.length; - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); + } - } - class CubicBezierCurve extends Curve { + scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - super(); + } - this.type = 'CubicBezierCurve'; + function sidewalls( contour, layeroffset ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + let i = contour.length; - } + while ( -- i >= 0 ) { - getPoint( t, optionalTarget = new Vector2() ) { + const j = i; + let k = i - 1; + if ( k < 0 ) k = contour.length - 1; - const point = optionalTarget; + //console.log('b', i,j, i-1, k,vertices.length); - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); + const slen1 = vlen * s; + const slen2 = vlen * ( s + 1 ); - return point; + const a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; - } + f4( a, b, c, d ); - copy( source ) { + } - super.copy( source ); + } - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + } - return this; + function v( x, y, z ) { - } + placeholder.push( x ); + placeholder.push( y ); + placeholder.push( z ); - toJSON() { + } - const data = super.toJSON(); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + function f3( a, b, c ) { - return data; + addVertex( a ); + addVertex( b ); + addVertex( c ); - } + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - fromJSON( json ) { + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); - super.fromJSON( json ); + } - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + function f4( a, b, c, d ) { - return this; + addVertex( a ); + addVertex( b ); + addVertex( d ); - } + addVertex( b ); + addVertex( c ); + addVertex( d ); - } - CubicBezierCurve.prototype.isCubicBezierCurve = true; + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - class CubicBezierCurve3 extends Curve { + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 3 ] ); - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); + addUV( uvs[ 3 ] ); - super(); + } - this.type = 'CubicBezierCurve3'; + function addVertex( index ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + verticesArray.push( placeholder[ index * 3 + 0 ] ); + verticesArray.push( placeholder[ index * 3 + 1 ] ); + verticesArray.push( placeholder[ index * 3 + 2 ] ); - } + } - getPoint( t, optionalTarget = new Vector3() ) { - const point = optionalTarget; + function addUV( vector2 ) { - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + uvArray.push( vector2.x ); + uvArray.push( vector2.y ); - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); + } - return point; + } } - copy( source ) { + toJSON() { - super.copy( source ); + const data = super.toJSON(); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + const shapes = this.parameters.shapes; + const options = this.parameters.options; - return this; + return toJSON$1( shapes, options, data ); } - toJSON() { + static fromJSON( data, shapes ) { - const data = super.toJSON(); + const geometryShapes = []; - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - return data; + const shape = shapes[ data.shapes[ j ] ]; - } + geometryShapes.push( shape ); - fromJSON( json ) { + } - super.fromJSON( json ); + const extrudePath = data.options.extrudePath; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + if ( extrudePath !== undefined ) { - return this; + data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - } + } - } + return new ExtrudeGeometry( geometryShapes, data.options ); - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; + } - class LineCurve extends Curve { + } - constructor( v1 = new Vector2(), v2 = new Vector2() ) { + const WorldUVGenerator = { - super(); + generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - this.type = 'LineCurve'; + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; - this.v1 = v1; - this.v2 = v2; + return [ + new Vector2( a_x, a_y ), + new Vector2( b_x, b_y ), + new Vector2( c_x, c_y ) + ]; - } + }, - getPoint( t, optionalTarget = new Vector2() ) { + generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - const point = optionalTarget; + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const a_z = vertices[ indexA * 3 + 2 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const b_z = vertices[ indexB * 3 + 2 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; + const c_z = vertices[ indexC * 3 + 2 ]; + const d_x = vertices[ indexD * 3 ]; + const d_y = vertices[ indexD * 3 + 1 ]; + const d_z = vertices[ indexD * 3 + 2 ]; - if ( t === 1 ) { + if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { - point.copy( this.v2 ); + return [ + new Vector2( a_x, 1 - a_z ), + new Vector2( b_x, 1 - b_z ), + new Vector2( c_x, 1 - c_z ), + new Vector2( d_x, 1 - d_z ) + ]; } else { - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + return [ + new Vector2( a_y, 1 - a_z ), + new Vector2( b_y, 1 - b_z ), + new Vector2( c_y, 1 - c_z ), + new Vector2( d_y, 1 - d_z ) + ]; } - return point; - } - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - - } + }; - getTangent( t, optionalTarget ) { + function toJSON$1( shapes, options, data ) { - const tangent = optionalTarget || new Vector2(); + data.shapes = []; - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); + if ( Array.isArray( shapes ) ) { - return tangent; + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - } + const shape = shapes[ i ]; - copy( source ) { + data.shapes.push( shape.uuid ); - super.copy( source ); + } - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + } else { - return this; + data.shapes.push( shapes.uuid ); } - toJSON() { + if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - const data = super.toJSON(); + return data; - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + } - return data; + class ShapeGeometry extends BufferGeometry { - } + constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { - fromJSON( json ) { + super(); + this.type = 'ShapeGeometry'; - super.fromJSON( json ); + this.parameters = { + shapes: shapes, + curveSegments: curveSegments + }; - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + // buffers - return this; + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - } + // helper variables - } + let groupStart = 0; + let groupCount = 0; - LineCurve.prototype.isLineCurve = true; + // allow single and array values for "shapes" parameter - class LineCurve3 extends Curve { + if ( Array.isArray( shapes ) === false ) { - constructor( v1 = new Vector3(), v2 = new Vector3() ) { + addShape( shapes ); - super(); + } else { - this.type = 'LineCurve3'; - this.isLineCurve3 = true; + for ( let i = 0; i < shapes.length; i ++ ) { - this.v1 = v1; - this.v2 = v2; + addShape( shapes[ i ] ); - } - getPoint( t, optionalTarget = new Vector3() ) { + this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - const point = optionalTarget; + groupStart += groupCount; + groupCount = 0; - if ( t === 1 ) { + } - point.copy( this.v2 ); + } - } else { + // build geometry - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - } - return point; + // helper functions - } - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { + function addShape( shape ) { - return this.getPoint( u, optionalTarget ); + const indexOffset = vertices.length / 3; + const points = shape.extractPoints( curveSegments ); - } - copy( source ) { + let shapeVertices = points.shape; + const shapeHoles = points.holes; - super.copy( source ); + // check direction of vertices - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - return this; + shapeVertices = shapeVertices.reverse(); - } - toJSON() { + } - const data = super.toJSON(); + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + const shapeHole = shapeHoles[ i ]; - return data; + if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - } - fromJSON( json ) { + shapeHoles[ i ] = shapeHole.reverse(); - super.fromJSON( json ); + } - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } - return this; + const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - } - - } - - class QuadraticBezierCurve extends Curve { + // join vertices of inner and outer paths to a single array - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - super(); + const shapeHole = shapeHoles[ i ]; + shapeVertices = shapeVertices.concat( shapeHole ); - this.type = 'QuadraticBezierCurve'; + } - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + // vertices, normals, uvs - } + for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - getPoint( t, optionalTarget = new Vector2() ) { + const vertex = shapeVertices[ i ]; - const point = optionalTarget; + vertices.push( vertex.x, vertex.y, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( vertex.x, vertex.y ); // world uvs - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + } - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); + // incides - return point; + for ( let i = 0, l = faces.length; i < l; i ++ ) { - } + const face = faces[ i ]; - copy( source ) { + const a = face[ 0 ] + indexOffset; + const b = face[ 1 ] + indexOffset; + const c = face[ 2 ] + indexOffset; - super.copy( source ); + indices.push( a, b, c ); + groupCount += 3; - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + } - return this; + } } @@ -35286,57 +35299,66 @@ const data = super.toJSON(); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + const shapes = this.parameters.shapes; - return data; + return toJSON( shapes, data ); } - fromJSON( json ) { + static fromJSON( data, shapes ) { - super.fromJSON( json ); + const geometryShapes = []; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - return this; + const shape = shapes[ data.shapes[ j ] ]; + + geometryShapes.push( shape ); + + } + + return new ShapeGeometry( geometryShapes, data.curveSegments ); } } - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; + function toJSON( shapes, data ) { - class QuadraticBezierCurve3 extends Curve { + data.shapes = []; - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { + if ( Array.isArray( shapes ) ) { - super(); + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - this.type = 'QuadraticBezierCurve3'; + const shape = shapes[ i ]; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + data.shapes.push( shape.uuid ); + + } + + } else { + + data.shapes.push( shapes.uuid ); } - getPoint( t, optionalTarget = new Vector3() ) { + return data; - const point = optionalTarget; + } - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + class ShadowMaterial extends Material { - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); + constructor( parameters ) { - return point; + super(); + + this.type = 'ShadowMaterial'; + + this.color = new Color( 0x000000 ); + this.transparent = true; + + this.setValues( parameters ); } @@ -35344,125 +35366,135 @@ super.copy( source ); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + this.color.copy( source.color ); return this; } - toJSON() { - - const data = super.toJSON(); - - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - - return data; + } - } + ShadowMaterial.prototype.isShadowMaterial = true; - fromJSON( json ) { + class RawShaderMaterial extends ShaderMaterial { - super.fromJSON( json ); + constructor( parameters ) { - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + super( parameters ); - return this; + this.type = 'RawShaderMaterial'; } } - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + RawShaderMaterial.prototype.isRawShaderMaterial = true; - class SplineCurve extends Curve { + class MeshStandardMaterial extends Material { - constructor( points = [] ) { + constructor( parameters ) { super(); - this.type = 'SplineCurve'; + this.defines = { 'STANDARD': '' }; - this.points = points; + this.type = 'MeshStandardMaterial'; - } + this.color = new Color( 0xffffff ); // diffuse + this.roughness = 1.0; + this.metalness = 0.0; - getPoint( t, optionalTarget = new Vector2() ) { + this.map = null; - const point = optionalTarget; + this.lightMap = null; + this.lightMapIntensity = 1.0; - const points = this.points; - const p = ( points.length - 1 ) * t; + this.aoMap = null; + this.aoMapIntensity = 1.0; - const intPoint = Math.floor( p ); - const weight = p - intPoint; + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + this.bumpMap = null; + this.bumpScale = 1; - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - return point; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.roughnessMap = null; - copy( source ) { + this.metalnessMap = null; - super.copy( source ); + this.alphaMap = null; - this.points = []; + this.envMap = null; + this.envMapIntensity = 1.0; - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - const point = source.points[ i ]; + this.flatShading = false; - this.points.push( point.clone() ); + this.setValues( parameters ); - } + } - return this; + copy( source ) { - } + super.copy( source ); - toJSON() { + this.defines = { 'STANDARD': '' }; - const data = super.toJSON(); + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; - data.points = []; + this.map = source.map; - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - const point = this.points[ i ]; - data.points.push( point.toArray() ); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - return data; + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - } + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - fromJSON( json ) { + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - super.fromJSON( json ); + this.roughnessMap = source.roughnessMap; - this.points = []; + this.metalnessMap = source.metalnessMap; - for ( let i = 0, l = json.points.length; i < l; i ++ ) { + this.alphaMap = source.alphaMap; - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.flatShading = source.flatShading; return this; @@ -35470,262 +35502,271 @@ } - SplineCurve.prototype.isSplineCurve = true; - - var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); - - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + MeshStandardMaterial.prototype.isMeshStandardMaterial = true; - class CurvePath extends Curve { + class MeshPhysicalMaterial extends MeshStandardMaterial { - constructor() { + constructor( parameters ) { super(); - this.type = 'CurvePath'; + this.defines = { - this.curves = []; - this.autoClose = false; // Automatically closes the path + 'STANDARD': '', + 'PHYSICAL': '' - } + }; - add( curve ) { + this.type = 'MeshPhysicalMaterial'; - this.curves.push( curve ); + this.clearcoatMap = null; + this.clearcoatRoughness = 0.0; + this.clearcoatRoughnessMap = null; + this.clearcoatNormalScale = new Vector2( 1, 1 ); + this.clearcoatNormalMap = null; - } + this.ior = 1.5; - closePath() { + Object.defineProperty( this, 'reflectivity', { + get: function () { - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); - if ( ! startPoint.equals( endPoint ) ) { + }, + set: function ( reflectivity ) { - this.curves.push( new LineCurve( endPoint, startPoint ) ); + this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); - } + } + } ); - } + this.sheenColor = new Color( 0x000000 ); + this.sheenColorMap = null; + this.sheenRoughness = 1.0; + this.sheenRoughnessMap = null; - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: + this.transmissionMap = null; - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') + this.thickness = 0; + this.thicknessMap = null; + this.attenuationDistance = 0.0; + this.attenuationColor = new Color( 1, 1, 1 ); - getPoint( t ) { + this.specularIntensity = 1.0; + this.specularIntensityMap = null; + this.specularColor = new Color( 1, 1, 1 ); + this.specularColorMap = null; - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; + this._sheen = 0.0; + this._clearcoat = 0; + this._transmission = 0; - // To think about boundaries points. + this.setValues( parameters ); - while ( i < curveLengths.length ) { + } - if ( curveLengths[ i ] >= d ) { + get sheen() { - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; + return this._sheen; - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + } - return curve.getPointAt( u ); + set sheen( value ) { - } + if ( this._sheen > 0 !== value > 0 ) { - i ++; + this.version ++; } - return null; - - // loop where sum != 0, sum > d , sum+1 0 !== value > 0 ) { - // Compute lengths and cache them - // We cannot overwrite getLengths() because UtoT mapping uses it. + this.version ++; - getCurveLengths() { + } - // We use cache values if curves and cache array are same length + this._clearcoat = value; - if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) { + } - return this.cacheLengths; + get transmission() { - } + return this._transmission; - // Get length of sub-curve - // Push sums into cached array + } - const lengths = []; - let sums = 0; + set transmission( value ) { - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { + if ( this._transmission > 0 !== value > 0 ) { - sums += this.curves[ i ].getLength(); - lengths.push( sums ); + this.version ++; } - this.cacheLengths = lengths; - - return lengths; + this._transmission = value; } - getSpacedPoints( divisions = 40 ) { - - const points = []; - - for ( let i = 0; i <= divisions; i ++ ) { + copy( source ) { - points.push( this.getPoint( i / divisions ) ); + super.copy( source ); - } + this.defines = { - if ( this.autoClose ) { + 'STANDARD': '', + 'PHYSICAL': '' - points.push( points[ 0 ] ); + }; - } + this.clearcoat = source.clearcoat; + this.clearcoatMap = source.clearcoatMap; + this.clearcoatRoughness = source.clearcoatRoughness; + this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; + this.clearcoatNormalMap = source.clearcoatNormalMap; + this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - return points; + this.ior = source.ior; - } + this.sheen = source.sheen; + this.sheenColor.copy( source.sheenColor ); + this.sheenColorMap = source.sheenColorMap; + this.sheenRoughness = source.sheenRoughness; + this.sheenRoughnessMap = source.sheenRoughnessMap; - getPoints( divisions = 12 ) { + this.transmission = source.transmission; + this.transmissionMap = source.transmissionMap; - const points = []; - let last; + this.thickness = source.thickness; + this.thicknessMap = source.thicknessMap; + this.attenuationDistance = source.attenuationDistance; + this.attenuationColor.copy( source.attenuationColor ); - for ( let i = 0, curves = this.curves; i < curves.length; i ++ ) { + this.specularIntensity = source.specularIntensity; + this.specularIntensityMap = source.specularIntensityMap; + this.specularColor.copy( source.specularColor ); + this.specularColorMap = source.specularColorMap; - const curve = curves[ i ]; - const resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2 - : ( curve && ( curve.isLineCurve || curve.isLineCurve3 ) ) ? 1 - : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length - : divisions; + return this; - const pts = curve.getPoints( resolution ); + } - for ( let j = 0; j < pts.length; j ++ ) { + } - const point = pts[ j ]; + MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates + class MeshPhongMaterial extends Material { - points.push( point ); - last = point; + constructor( parameters ) { - } + super(); - } + this.type = 'MeshPhongMaterial'; - if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { + this.color = new Color( 0xffffff ); // diffuse + this.specular = new Color( 0x111111 ); + this.shininess = 30; - points.push( points[ 0 ] ); + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - return points; + this.aoMap = null; + this.aoMapIntensity = 1.0; - } + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - copy( source ) { + this.bumpMap = null; + this.bumpScale = 1; - super.copy( source ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - this.curves = []; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { + this.specularMap = null; - const curve = source.curves[ i ]; + this.alphaMap = null; - this.curves.push( curve.clone() ); + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - } + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - this.autoClose = source.autoClose; + this.flatShading = false; - return this; + this.setValues( parameters ); } - toJSON() { + copy( source ) { - const data = super.toJSON(); + super.copy( source ); - data.autoClose = this.autoClose; - data.curves = []; + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { + this.map = source.map; - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - } + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - return data; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - } + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - fromJSON( json ) { + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - super.fromJSON( json ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - this.autoClose = json.autoClose; - this.curves = []; + this.specularMap = source.specularMap; - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { + this.alphaMap = source.alphaMap; - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.flatShading = source.flatShading; return this; @@ -35733,156 +35774,195 @@ } - class Path extends CurvePath { + MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - constructor( points ) { + class MeshToonMaterial extends Material { + + constructor( parameters ) { super(); - this.type = 'Path'; - this.currentPoint = new Vector2(); + this.defines = { 'TOON': '' }; - if ( points ) { + this.type = 'MeshToonMaterial'; - this.setFromPoints( points ); + this.color = new Color( 0xffffff ); - } + this.map = null; + this.gradientMap = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - setFromPoints( points ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - this.moveTo( points[ 0 ].x, points[ 0 ].y ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - for ( let i = 1, l = points.length; i < l; i ++ ) { + this.bumpMap = null; + this.bumpScale = 1; - this.lineTo( points[ i ].x, points[ i ].y ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - return this; + this.alphaMap = null; - } + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - moveTo( x, y ) { + this.setValues( parameters ); - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + } - return this; + copy( source ) { - } + super.copy( source ); - lineTo( x, y ) { + this.color.copy( source.color ); - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); + this.map = source.map; + this.gradientMap = source.gradientMap; - this.currentPoint.set( x, y ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - return this; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - quadraticCurveTo( aCPx, aCPy, aX, aY ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - this.curves.push( curve ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - this.currentPoint.set( aX, aY ); + this.alphaMap = source.alphaMap; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; return this; } - bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + } - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); + MeshToonMaterial.prototype.isMeshToonMaterial = true; - this.curves.push( curve ); + class MeshNormalMaterial extends Material { - this.currentPoint.set( aX, aY ); + constructor( parameters ) { - return this; + super(); - } + this.type = 'MeshNormalMaterial'; - splineThru( pts /*Array of Vector*/ ) { + this.bumpMap = null; + this.bumpScale = 1; - const npts = [ this.currentPoint.clone() ].concat( pts ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - const curve = new SplineCurve( npts ); - this.curves.push( curve ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - this.currentPoint.copy( pts[ pts.length - 1 ] ); + this.wireframe = false; + this.wireframeLinewidth = 1; - return this; + this.fog = false; + + this.flatShading = false; + + this.setValues( parameters ); } - arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + copy( source ) { - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + super.copy( source ); - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - return this; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - } + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + this.flatShading = source.flatShading; return this; } - ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + } - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + class MeshLambertMaterial extends Material { - return this; + constructor( parameters ) { - } + super(); - absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + this.type = 'MeshLambertMaterial'; - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + this.color = new Color( 0xffffff ); // diffuse - if ( this.curves.length > 0 ) { + this.map = null; - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); + this.lightMap = null; + this.lightMapIntensity = 1.0; - if ( ! firstPoint.equals( this.currentPoint ) ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - this.lineTo( firstPoint.x, firstPoint.y ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - } + this.specularMap = null; - } + this.alphaMap = null; - this.curves.push( curve ); + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - return this; + this.setValues( parameters ); } @@ -35890,27 +35970,33 @@ super.copy( source ); - this.currentPoint.copy( source.currentPoint ); - - return this; - - } + this.color.copy( source.color ); - toJSON() { + this.map = source.map; - const data = super.toJSON(); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - data.currentPoint = this.currentPoint.toArray(); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - return data; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - } + this.specularMap = source.specularMap; - fromJSON( json ) { + this.alphaMap = source.alphaMap; - super.fromJSON( json ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - this.currentPoint.fromArray( json.currentPoint ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; return this; @@ -35918,96 +36004,102 @@ } - class Shape extends Path { + MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - constructor( points ) { + class MeshMatcapMaterial extends Material { - super( points ); + constructor( parameters ) { - this.uuid = generateUUID(); + super(); - this.type = 'Shape'; + this.defines = { 'MATCAP': '' }; - this.holes = []; + this.type = 'MeshMatcapMaterial'; - } + this.color = new Color( 0xffffff ); // diffuse - getPointsHoles( divisions ) { + this.matcap = null; - const holesPts = []; + this.map = null; - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + this.bumpMap = null; + this.bumpScale = 1; - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - return holesPts; + this.alphaMap = null; - } + this.flatShading = false; - // get points of shape and holes (keypoints based on segments parameter) + this.setValues( parameters ); - extractPoints( divisions ) { + } - return { - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) + copy( source ) { - }; + super.copy( source ); - } + this.defines = { 'MATCAP': '' }; - copy( source ) { + this.color.copy( source.color ); - super.copy( source ); + this.matcap = source.matcap; - this.holes = []; + this.map = source.map; - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - const hole = source.holes[ i ]; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - this.holes.push( hole.clone() ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.alphaMap = source.alphaMap; + + this.flatShading = source.flatShading; return this; } - toJSON() { - - const data = super.toJSON(); - - data.uuid = this.uuid; - data.holes = []; + } - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); + class LineDashedMaterial extends LineBasicMaterial { - } + constructor( parameters ) { - return data; + super(); - } + this.type = 'LineDashedMaterial'; - fromJSON( json ) { + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - super.fromJSON( json ); + this.setValues( parameters ); - this.uuid = json.uuid; - this.holes = []; + } - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { + copy( source ) { - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); + super.copy( source ); - } + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; return this; @@ -36015,1673 +36107,1735 @@ } - class Light extends Object3D { - - constructor( color, intensity = 1 ) { - - super(); + LineDashedMaterial.prototype.isLineDashedMaterial = true; - this.type = 'Light'; + const materialLib = { + ShadowMaterial, + SpriteMaterial, + RawShaderMaterial, + ShaderMaterial, + PointsMaterial, + MeshPhysicalMaterial, + MeshStandardMaterial, + MeshPhongMaterial, + MeshToonMaterial, + MeshNormalMaterial, + MeshLambertMaterial, + MeshDepthMaterial, + MeshDistanceMaterial, + MeshBasicMaterial, + MeshMatcapMaterial, + LineDashedMaterial, + LineBasicMaterial, + Material + }; - this.color = new Color( color ); - this.intensity = intensity; + Material.fromType = function ( type ) { - } + return new materialLib[ type ](); - dispose() { + }; - // Empty here in base class; some subclasses override. + const AnimationUtils = { - } + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function ( array, from, to ) { - copy( source ) { + if ( AnimationUtils.isTypedArray( array ) ) { - super.copy( source ); + // in ios9 array.subarray(from, undefined) will return empty array + // but array.subarray(from) or array.subarray(from, len) is correct + return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - this.color.copy( source.color ); - this.intensity = source.intensity; + } - return this; + return array.slice( from, to ); - } + }, - toJSON( meta ) { + // converts an array to a specific type + convertArray: function ( array, type, forceClone ) { - const data = super.toJSON( meta ); + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + return new type( array ); // create typed array - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; + } - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); + return Array.prototype.slice.call( array ); // create Array - return data; + }, - } + isTypedArray: function ( object ) { - } + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); - Light.prototype.isLight = true; + }, - class HemisphereLight extends Light { + // returns an array by which times and values can be sorted + getKeyframeOrder: function ( times ) { - constructor( skyColor, groundColor, intensity ) { + function compareTime( i, j ) { - super( skyColor, intensity ); + return times[ i ] - times[ j ]; - this.type = 'HemisphereLight'; + } - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + const n = times.length; + const result = new Array( n ); + for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - this.groundColor = new Color( groundColor ); + result.sort( compareTime ); - } + return result; - copy( source ) { + }, - Light.prototype.copy.call( this, source ); + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function ( values, stride, order ) { - this.groundColor.copy( source.groundColor ); + const nValues = values.length; + const result = new values.constructor( nValues ); - return this; + for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - } + const srcOffset = order[ i ] * stride; - } + for ( let j = 0; j !== stride; ++ j ) { - HemisphereLight.prototype.isHemisphereLight = true; + result[ dstOffset ++ ] = values[ srcOffset + j ]; - const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); - const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); - const _lookTarget$1 = /*@__PURE__*/ new Vector3(); + } - class LightShadow { + } - constructor( camera ) { + return result; - this.camera = camera; + }, - this.bias = 0; - this.normalBias = 0; - this.radius = 1; + // function for parsing AOS keyframe formats + flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - this.mapSize = new Vector2( 512, 512 ); + let i = 1, key = jsonKeys[ 0 ]; - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - this.autoUpdate = true; - this.needsUpdate = false; + key = jsonKeys[ i ++ ]; - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); + } - this._viewportCount = 1; + if ( key === undefined ) return; // no data - this._viewports = [ + let value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - new Vector4( 0, 0, 1, 1 ) + if ( Array.isArray( value ) ) { - ]; + do { - } + value = key[ valuePropertyName ]; - getViewportCount() { + if ( value !== undefined ) { - return this._viewportCount; + times.push( key.time ); + values.push.apply( values, value ); // push all elements - } + } - getFrustum() { + key = jsonKeys[ i ++ ]; - return this._frustum; + } while ( key !== undefined ); - } + } else if ( value.toArray !== undefined ) { - updateMatrices( light ) { + // ...assume THREE.Math-ish - const shadowCamera = this.camera; - const shadowMatrix = this.matrix; + do { - _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld$1 ); + value = key[ valuePropertyName ]; - _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _lookTarget$1 ); - shadowCamera.updateMatrixWorld(); + if ( value !== undefined ) { - _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); + times.push( key.time ); + value.toArray( values, values.length ); - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); + } - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + key = jsonKeys[ i ++ ]; - } + } while ( key !== undefined ); - getViewport( viewportIndex ) { + } else { - return this._viewports[ viewportIndex ]; + // otherwise push as-is - } + do { - getFrameExtents() { + value = key[ valuePropertyName ]; - return this._frameExtents; + if ( value !== undefined ) { - } + times.push( key.time ); + values.push( value ); - dispose() { + } - if ( this.map ) { + key = jsonKeys[ i ++ ]; - this.map.dispose(); + } while ( key !== undefined ); } - if ( this.mapPass ) { + }, - this.mapPass.dispose(); + subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { - } + const clip = sourceClip.clone(); - } + clip.name = name; - copy( source ) { + const tracks = []; - this.camera = source.camera.clone(); + for ( let i = 0; i < clip.tracks.length; ++ i ) { - this.bias = source.bias; - this.radius = source.radius; + const track = clip.tracks[ i ]; + const valueSize = track.getValueSize(); - this.mapSize.copy( source.mapSize ); + const times = []; + const values = []; - return this; + for ( let j = 0; j < track.times.length; ++ j ) { - } + const frame = track.times[ j ] * fps; - clone() { + if ( frame < startFrame || frame >= endFrame ) continue; - return new this.constructor().copy( this ); + times.push( track.times[ j ] ); - } + for ( let k = 0; k < valueSize; ++ k ) { - toJSON() { + values.push( track.values[ j * valueSize + k ] ); - const object = {}; + } - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + } - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + if ( times.length === 0 ) continue; - return object; + track.times = AnimationUtils.convertArray( times, track.times.constructor ); + track.values = AnimationUtils.convertArray( values, track.values.constructor ); - } + tracks.push( track ); - } + } - class SpotLightShadow extends LightShadow { + clip.tracks = tracks; - constructor() { + // find minimum .times value across all tracks in the trimmed clip - super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + let minStartTime = Infinity; - this.focus = 1; + for ( let i = 0; i < clip.tracks.length; ++ i ) { - } + if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - updateMatrices( light ) { + minStartTime = clip.tracks[ i ].times[ 0 ]; - const camera = this.camera; + } - const fov = RAD2DEG * 2 * light.angle * this.focus; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; + } - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + // shift all tracks such that clip begins at t=0 - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); + for ( let i = 0; i < clip.tracks.length; ++ i ) { + + clip.tracks[ i ].shift( - 1 * minStartTime ); } - super.updateMatrices( light ); + clip.resetDuration(); - } + return clip; - copy( source ) { + }, - super.copy( source ); + makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { - this.focus = source.focus; + if ( fps <= 0 ) fps = 30; - return this; + const numTracks = referenceClip.tracks.length; + const referenceTime = referenceFrame / fps; - } + // Make each track's values relative to the values at the reference frame + for ( let i = 0; i < numTracks; ++ i ) { - } + const referenceTrack = referenceClip.tracks[ i ]; + const referenceTrackType = referenceTrack.ValueTypeName; - SpotLightShadow.prototype.isSpotLightShadow = true; + // Skip this track if it's non-numeric + if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - class SpotLight extends Light { + // Find the track in the target clip whose name and type matches the reference track + const targetTrack = targetClip.tracks.find( function ( track ) { - constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { + return track.name === referenceTrack.name + && track.ValueTypeName === referenceTrackType; - super( color, intensity ); + } ); - this.type = 'SpotLight'; + if ( targetTrack === undefined ) continue; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + let referenceOffset = 0; + const referenceValueSize = referenceTrack.getValueSize(); - this.target = new Object3D(); + if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - this.distance = distance; - this.angle = angle; - this.penumbra = penumbra; - this.decay = decay; // for physically correct lights, should be 2. + referenceOffset = referenceValueSize / 3; - this.shadow = new SpotLightShadow(); + } - } + let targetOffset = 0; + const targetValueSize = targetTrack.getValueSize(); - get power() { + if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; + targetOffset = targetValueSize / 3; - } + } - set power( power ) { + const lastIndex = referenceTrack.times.length - 1; + let referenceValue; - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; + // Find the value to subtract out of the track + if ( referenceTime <= referenceTrack.times[ 0 ] ) { - } + // Reference frame is earlier than the first keyframe, so just use the first keyframe + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - dispose() { + } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - this.shadow.dispose(); + // Reference frame is after the last keyframe, so just use the last keyframe + const startIndex = lastIndex * referenceValueSize + referenceOffset; + const endIndex = startIndex + referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - } + } else { - copy( source ) { + // Interpolate to the reference value + const interpolant = referenceTrack.createInterpolant(); + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + interpolant.evaluate( referenceTime ); + referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - super.copy( source ); + } - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + // Conjugate the quaternion + if ( referenceTrackType === 'quaternion' ) { - this.target = source.target.clone(); + const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); + referenceQuat.toArray( referenceValue ); - this.shadow = source.shadow.clone(); + } - return this; + // Subtract the reference value from all of the track values - } + const numTimes = targetTrack.times.length; + for ( let j = 0; j < numTimes; ++ j ) { - } + const valueStart = j * targetValueSize + targetOffset; - SpotLight.prototype.isSpotLight = true; + if ( referenceTrackType === 'quaternion' ) { - const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); - const _lightPositionWorld = /*@__PURE__*/ new Vector3(); - const _lookTarget = /*@__PURE__*/ new Vector3(); + // Multiply the conjugate for quaternion track types + Quaternion.multiplyQuaternionsFlat( + targetTrack.values, + valueStart, + referenceValue, + 0, + targetTrack.values, + valueStart + ); - class PointLightShadow extends LightShadow { + } else { - constructor() { + const valueEnd = targetValueSize - targetOffset * 2; - super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + // Subtract each value for all other numeric track types + for ( let k = 0; k < valueEnd; ++ k ) { - this._frameExtents = new Vector2( 4, 2 ); + targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - this._viewportCount = 6; + } - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction + } - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; + } - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; + } - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; + targetClip.blendMode = AdditiveAnimationBlendMode; + + return targetClip; } - updateMatrices( light, viewportIndex = 0 ) { + }; - const camera = this.camera; - const shadowMatrix = this.matrix; + /** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + */ - const far = light.distance || camera.far; + class Interpolant { - if ( far !== camera.far ) { + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - camera.far = far; - camera.updateProjectionMatrix(); + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - } + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( _lightPositionWorld ); + this.settings = null; + this.DefaultSettings_ = {}; - _lookTarget.copy( camera.position ); - _lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( _lookTarget ); - camera.updateMatrixWorld(); + } - shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); + evaluate( t ) { - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix ); + const pp = this.parameterPositions; + let i1 = this._cachedIndex, + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - } + validate_interval: { - } + seek: { - PointLightShadow.prototype.isPointLightShadow = true; + let right; - class PointLight extends Light { + linear_scan: { - constructor( color, intensity, distance = 0, decay = 1 ) { + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - super( color, intensity ); + for ( let giveUpAt = i1 + 2; ; ) { - this.type = 'PointLight'; + if ( t1 === undefined ) { - this.distance = distance; - this.decay = decay; // for physically correct lights, should be 2. + if ( t < t0 ) break forward_scan; - this.shadow = new PointLightShadow(); + // after end - } + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); - get power() { + } - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; + if ( i1 === giveUpAt ) break; // this loop - } + t0 = t1; + t1 = pp[ ++ i1 ]; - set power( power ) { + if ( t < t1 ) { - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); + // we have arrived at the sought interval + break seek; - } + } - dispose() { + } - this.shadow.dispose(); + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - } + } - copy( source ) { + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - super.copy( source ); + // looping? - this.distance = source.distance; - this.decay = source.decay; + const t1global = pp[ 1 ]; - this.shadow = source.shadow.clone(); + if ( t < t1global ) { - return this; + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - } + } - } + // linear reverse scan - PointLight.prototype.isPointLight = true; + for ( let giveUpAt = i1 - 2; ; ) { - class OrthographicCamera extends Camera { + if ( t0 === undefined ) { - constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { + // before start - super(); + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - this.type = 'OrthographicCamera'; + } - this.zoom = 1; - this.view = null; + if ( i1 === giveUpAt ) break; // this loop - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; + t1 = t0; + t0 = pp[ -- i1 - 1 ]; - this.near = near; - this.far = far; + if ( t >= t0 ) { - this.updateProjectionMatrix(); + // we have arrived at the sought interval + break seek; - } + } - copy( source, recursive ) { + } - super.copy( source, recursive ); + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; + } - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); + // the interval is valid - return this; + break validate_interval; - } + } // linear scan - setViewOffset( fullWidth, fullHeight, x, y, width, height ) { + // binary search - if ( this.view === null ) { + while ( i1 < right ) { - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; + const mid = ( i1 + right ) >>> 1; - } + if ( t < pp[ mid ] ) { - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; + right = mid; - this.updateProjectionMatrix(); + } else { - } + i1 = mid + 1; - clearViewOffset() { + } - if ( this.view !== null ) { + } - this.view.enabled = false; + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - } + // check boundary cases, again - this.updateProjectionMatrix(); + if ( t0 === undefined ) { - } + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - updateProjectionMatrix() { + } - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; + if ( t1 === undefined ) { - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); - if ( this.view !== null && this.view.enabled ) { + } - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; + } // seek - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; + this._cachedIndex = i1; - } + this.intervalChanged_( i1, t0, t1 ); - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); + } // validate_interval - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); + return this.interpolate_( i1, t0, t, t1 ); } - toJSON( meta ) { + getSettings_() { - const data = super.toJSON( meta ); + return this.settings || this.DefaultSettings_; - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; + } - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + copySampleValue_( index ) { - return data; + // copies a sample value to the result buffer - } + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - } + for ( let i = 0; i !== stride; ++ i ) { - OrthographicCamera.prototype.isOrthographicCamera = true; + result[ i ] = values[ offset + i ]; - class DirectionalLightShadow extends LightShadow { + } - constructor() { - - super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + return result; } - } + // Template methods for derived classes: - DirectionalLightShadow.prototype.isDirectionalLightShadow = true; + interpolate_( /* i1, t0, t, t1 */ ) { - class DirectionalLight extends Light { + throw new Error( 'call to abstract method' ); + // implementations shall return this.resultBuffer - constructor( color, intensity ) { + } - super( color, intensity ); + intervalChanged_( /* i1, t0, t1 */ ) { - this.type = 'DirectionalLight'; + // empty - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + } - this.target = new Object3D(); + } - this.shadow = new DirectionalLightShadow(); + // ALIAS DEFINITIONS - } + Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; + Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; - dispose() { + /** + * Fast and simple cubic spline interpolant. + * + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. + */ - this.shadow.dispose(); + class CubicInterpolant extends Interpolant { - } + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - copy( source ) { + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - super.copy( source ); + this._weightPrev = - 0; + this._offsetPrev = - 0; + this._weightNext = - 0; + this._offsetNext = - 0; - this.target = source.target.clone(); - this.shadow = source.shadow.clone(); + this.DefaultSettings_ = { - return this; + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + + }; } - } + intervalChanged_( i1, t0, t1 ) { - DirectionalLight.prototype.isDirectionalLight = true; + const pp = this.parameterPositions; + let iPrev = i1 - 2, + iNext = i1 + 1, - class AmbientLight extends Light { + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; - constructor( color, intensity ) { + if ( tPrev === undefined ) { - super( color, intensity ); + switch ( this.getSettings_().endingStart ) { - this.type = 'AmbientLight'; + case ZeroSlopeEnding: - } + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; - } + break; - AmbientLight.prototype.isAmbientLight = true; + case WrapAroundEnding: - class RectAreaLight extends Light { + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - constructor( color, intensity, width = 10, height = 10 ) { + break; - super( color, intensity ); + default: // ZeroCurvatureEnding - this.type = 'RectAreaLight'; + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; - this.width = width; - this.height = height; + } - } + } - copy( source ) { + if ( tNext === undefined ) { - super.copy( source ); + switch ( this.getSettings_().endingEnd ) { - this.width = source.width; - this.height = source.height; + case ZeroSlopeEnding: - return this; + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; - } + break; - toJSON( meta ) { + case WrapAroundEnding: - const data = super.toJSON( meta ); + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; - data.object.width = this.width; - data.object.height = this.height; + break; - return data; + default: // ZeroCurvatureEnding - } + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; - } + } - RectAreaLight.prototype.isRectAreaLight = true; + } - /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ + const halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; - // 3-band SH defined by 9 coefficients + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; - class SphericalHarmonics3 { + } - constructor() { + interpolate_( i1, t0, t, t1 ) { - this.coefficients = []; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - for ( let i = 0; i < 9; i ++ ) { + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, - this.coefficients.push( new Vector3() ); + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; - } + // evaluate polynomials - } + const sP = - wP * ppp + 2 * wP * pp - wP * p; + const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; + const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + const sN = wN * ppp - wN * pp; - set( coefficients ) { + // combine data linearly - for ( let i = 0; i < 9; i ++ ) { + for ( let i = 0; i !== stride; ++ i ) { - this.coefficients[ i ].copy( coefficients[ i ] ); + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; } - return this; + return result; } - zero() { - - for ( let i = 0; i < 9; i ++ ) { + } - this.coefficients[ i ].set( 0, 0, 0 ); + class LinearInterpolant extends Interpolant { - } + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - return this; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { + interpolate_( i1, t0, t, t1 ) { - // normal is assumed to be unit length + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - const x = normal.x, y = normal.y, z = normal.z; + offset1 = i1 * stride, + offset0 = offset1 - stride, - const coeff = this.coefficients; + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); + for ( let i = 0; i !== stride; ++ i ) { - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); + } - return target; + return result; } - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { + } - // normal is assumed to be unit length + /** + * + * Interpolant that evaluates to the sample value at the position preceding + * the parameter. + */ - const x = normal.x, y = normal.y, z = normal.z; + class DiscreteInterpolant extends Interpolant { - const coeff = this.coefficients; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); + } - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 + interpolate_( i1 /*, t0, t, t1 */ ) { - return target; + return this.copySampleValue_( i1 - 1 ); } - add( sh ) { + } - for ( let i = 0; i < 9; i ++ ) { + class KeyframeTrack { - this.coefficients[ i ].add( sh.coefficients[ i ] ); + constructor( name, times, values, interpolation ) { - } + if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); + if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - return this; + this.name = name; - } + this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - addScaledSH( sh, s ) { + this.setInterpolation( interpolation || this.DefaultInterpolation ); - for ( let i = 0; i < 9; i ++ ) { + } - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): - } + static toJSON( track ) { - return this; + const trackType = track.constructor; - } + let json; - scale( s ) { + // derived classes can define a static toJSON method + if ( trackType.toJSON !== this.toJSON ) { - for ( let i = 0; i < 9; i ++ ) { + json = trackType.toJSON( track ); - this.coefficients[ i ].multiplyScalar( s ); + } else { - } + // by default, we assume the data can be serialized as-is + json = { - return this; + 'name': track.name, + 'times': AnimationUtils.convertArray( track.times, Array ), + 'values': AnimationUtils.convertArray( track.values, Array ) - } + }; - lerp( sh, alpha ) { + const interpolation = track.getInterpolation(); - for ( let i = 0; i < 9; i ++ ) { + if ( interpolation !== track.DefaultInterpolation ) { - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); + json.interpolation = interpolation; + + } } - return this; + json.type = track.ValueTypeName; // mandatory + + return json; } - equals( sh ) { + InterpolantFactoryMethodDiscrete( result ) { - for ( let i = 0; i < 9; i ++ ) { + return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { + } - return false; + InterpolantFactoryMethodLinear( result ) { - } + return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - } + } - return true; + InterpolantFactoryMethodSmooth( result ) { + + return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); } - copy( sh ) { + setInterpolation( interpolation ) { - return this.set( sh.coefficients ); + let factoryMethod; - } + switch ( interpolation ) { - clone() { + case InterpolateDiscrete: - return new this.constructor().copy( this ); + factoryMethod = this.InterpolantFactoryMethodDiscrete; - } + break; - fromArray( array, offset = 0 ) { + case InterpolateLinear: - const coefficients = this.coefficients; + factoryMethod = this.InterpolantFactoryMethodLinear; - for ( let i = 0; i < 9; i ++ ) { + break; - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); + case InterpolateSmooth: - } + factoryMethod = this.InterpolantFactoryMethodSmooth; - return this; + break; - } + } - toArray( array = [], offset = 0 ) { + if ( factoryMethod === undefined ) { - const coefficients = this.coefficients; + const message = 'unsupported interpolation for ' + + this.ValueTypeName + ' keyframe track named ' + this.name; - for ( let i = 0; i < 9; i ++ ) { + if ( this.createInterpolant === undefined ) { - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { - } + this.setInterpolation( this.DefaultInterpolation ); - return array; + } else { - } + throw new Error( message ); // fatal, in this case - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { + } - // normal is assumed to be unit length + } - const x = normal.x, y = normal.y, z = normal.z; + console.warn( 'THREE.KeyframeTrack:', message ); + return this; - // band 0 - shBasis[ 0 ] = 0.282095; + } - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; + this.createInterpolant = factoryMethod; - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); + return this; } - } + getInterpolation() { - SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; + switch ( this.createInterpolant ) { - class LightProbe extends Light { + case this.InterpolantFactoryMethodDiscrete: - constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { + return InterpolateDiscrete; - super( undefined, intensity ); + case this.InterpolantFactoryMethodLinear: - this.sh = sh; + return InterpolateLinear; - } + case this.InterpolantFactoryMethodSmooth: - copy( source ) { + return InterpolateSmooth; - super.copy( source ); + } - this.sh.copy( source.sh ); + } - return this; + getValueSize() { + + return this.values.length / this.times.length; } - fromJSON( json ) { + // move all keyframes either forwards or backwards in time + shift( timeOffset ) { - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); + if ( timeOffset !== 0.0 ) { - return this; + const times = this.times; - } + for ( let i = 0, n = times.length; i !== n; ++ i ) { - toJSON( meta ) { + times[ i ] += timeOffset; - const data = super.toJSON( meta ); + } - data.object.sh = this.sh.toArray(); + } - return data; + return this; } - } + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale( timeScale ) { - LightProbe.prototype.isLightProbe = true; + if ( timeScale !== 1.0 ) { - class LoaderUtils { + const times = this.times; - static decodeText( array ) { + for ( let i = 0, n = times.length; i !== n; ++ i ) { - if ( typeof TextDecoder !== 'undefined' ) { + times[ i ] *= timeScale; - return new TextDecoder().decode( array ); + } } - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. - - let s = ''; - - for ( let i = 0, il = array.length; i < il; i ++ ) { - - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); + return this; - } + } - try { + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim( startTime, endTime ) { - // merges multi-byte utf-8 characters. + const times = this.times, + nKeys = times.length; - return decodeURIComponent( escape( s ) ); + let from = 0, + to = nKeys - 1; - } catch ( e ) { // see #16358 + while ( from !== nKeys && times[ from ] < startTime ) { - return s; + ++ from; } - } + while ( to !== - 1 && times[ to ] > endTime ) { - static extractUrlBase( url ) { + -- to; - const index = url.lastIndexOf( '/' ); + } - if ( index === - 1 ) return './'; + ++ to; // inclusive -> exclusive bound - return url.substr( 0, index + 1 ); + if ( from !== 0 || to !== nKeys ) { - } + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) { - } + to = Math.max( to, 1 ); + from = to - 1; - class InstancedBufferGeometry extends BufferGeometry { + } - constructor() { + const stride = this.getValueSize(); + this.times = AnimationUtils.arraySlice( times, from, to ); + this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - super(); + } - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; + return this; } - copy( source ) { + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate() { - super.copy( source ); + let valid = true; - this.instanceCount = source.instanceCount; + const valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - return this; + console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); + valid = false; - } + } - clone() { + const times = this.times, + values = this.values, - return new this.constructor().copy( this ); + nKeys = times.length; - } + if ( nKeys === 0 ) { - toJSON() { + console.error( 'THREE.KeyframeTrack: Track is empty.', this ); + valid = false; - const data = super.toJSON( this ); + } - data.instanceCount = this.instanceCount; + let prevTime = null; - data.isInstancedBufferGeometry = true; + for ( let i = 0; i !== nKeys; i ++ ) { - return data; + const currTime = times[ i ]; - } + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - } + console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); + valid = false; + break; - InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; + } - class InstancedBufferAttribute extends BufferAttribute { + if ( prevTime !== null && prevTime > currTime ) { - constructor( array, itemSize, normalized, meshPerAttribute ) { + console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); + valid = false; + break; - if ( typeof ( normalized ) === 'number' ) { + } - meshPerAttribute = normalized; + prevTime = currTime; - normalized = false; + } - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); + if ( values !== undefined ) { - } + if ( AnimationUtils.isTypedArray( values ) ) { - super( array, itemSize, normalized ); + for ( let i = 0, n = values.length; i !== n; ++ i ) { - this.meshPerAttribute = meshPerAttribute || 1; + const value = values[ i ]; - } + if ( isNaN( value ) ) { - copy( source ) { + console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); + valid = false; + break; - super.copy( source ); + } - this.meshPerAttribute = source.meshPerAttribute; + } - return this; + } - } + } - toJSON() { + return valid; - const data = super.toJSON(); + } - data.meshPerAttribute = this.meshPerAttribute; + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize() { - data.isInstancedBufferAttribute = true; + // times or values may be shared with other tracks, so overwriting is unsafe + const times = AnimationUtils.arraySlice( this.times ), + values = AnimationUtils.arraySlice( this.values ), + stride = this.getValueSize(), - return data; + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - } + lastIndex = times.length - 1; - } + let writeIndex = 1; - InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; + for ( let i = 1; i < lastIndex; ++ i ) { - class ImageBitmapLoader extends Loader { + let keep = false; - constructor( manager ) { + const time = times[ i ]; + const timeNext = times[ i + 1 ]; - super( manager ); + // remove adjacent keyframes scheduled at the same time - if ( typeof createImageBitmap === 'undefined' ) { + if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); + if ( ! smoothInterpolation ) { - } + // remove unnecessary keyframes same as their neighbors - if ( typeof fetch === 'undefined' ) { + const offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + for ( let j = 0; j !== stride; ++ j ) { - } + const value = values[ offset + j ]; - this.options = { premultiplyAlpha: 'none' }; + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - } + keep = true; + break; - setOptions( options ) { + } - this.options = options; + } - return this; + } else { - } + keep = true; - load( url, onLoad, onProgress, onError ) { + } - if ( url === undefined ) url = ''; + } - if ( this.path !== undefined ) url = this.path + url; + // in-place compaction - url = this.manager.resolveURL( url ); + if ( keep ) { - const scope = this; + if ( i !== writeIndex ) { - const cached = Cache.get( url ); + times[ writeIndex ] = times[ i ]; - if ( cached !== undefined ) { + const readOffset = i * stride, + writeOffset = writeIndex * stride; - scope.manager.itemStart( url ); + for ( let j = 0; j !== stride; ++ j ) { - setTimeout( function () { + values[ writeOffset + j ] = values[ readOffset + j ]; - if ( onLoad ) onLoad( cached ); + } - scope.manager.itemEnd( url ); + } - }, 0 ); + ++ writeIndex; - return cached; + } } - const fetchOptions = {}; - fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; - fetchOptions.headers = this.requestHeader; + // flush last keyframe (compaction looks ahead) - fetch( url, fetchOptions ).then( function ( res ) { + if ( lastIndex > 0 ) { - return res.blob(); + times[ writeIndex ] = times[ lastIndex ]; - } ).then( function ( blob ) { + for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); + values[ writeOffset + j ] = values[ readOffset + j ]; - } ).then( function ( imageBitmap ) { + } - Cache.add( url, imageBitmap ); + ++ writeIndex; - if ( onLoad ) onLoad( imageBitmap ); + } - scope.manager.itemEnd( url ); + if ( writeIndex !== times.length ) { - } ).catch( function ( e ) { + this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - if ( onError ) onError( e ); + } else { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + this.times = times; + this.values = values; - } ); + } - scope.manager.itemStart( url ); + return this; } - } - - ImageBitmapLoader.prototype.isImageBitmapLoader = true; - - let _context; - - const AudioContext = { + clone() { - getContext: function () { + const times = AnimationUtils.arraySlice( this.times, 0 ); + const values = AnimationUtils.arraySlice( this.values, 0 ); - if ( _context === undefined ) { + const TypedKeyframeTrack = this.constructor; + const track = new TypedKeyframeTrack( this.name, times, values ); - _context = new ( window.AudioContext || window.webkitAudioContext )(); + // Interpolant argument to constructor is not saved, so copy the factory method directly. + track.createInterpolant = this.createInterpolant; - } + return track; - return _context; + } - }, + } - setContext: function ( value ) { + KeyframeTrack.prototype.TimeBufferType = Float32Array; + KeyframeTrack.prototype.ValueBufferType = Float32Array; + KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - _context = value; + /** + * A Track of Boolean keyframe values. + */ + class BooleanKeyframeTrack extends KeyframeTrack {} - } + BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; + BooleanKeyframeTrack.prototype.ValueBufferType = Array; + BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - }; + /** + * A Track of keyframe values that represent color. + */ + class ColorKeyframeTrack extends KeyframeTrack {} - class AudioLoader extends Loader { + ColorKeyframeTrack.prototype.ValueTypeName = 'color'; - constructor( manager ) { + /** + * A Track of numeric keyframe values. + */ + class NumberKeyframeTrack extends KeyframeTrack {} - super( manager ); + NumberKeyframeTrack.prototype.ValueTypeName = 'number'; - } + /** + * Spherical linear unit quaternion interpolant. + */ - load( url, onLoad, onProgress, onError ) { + class QuaternionLinearInterpolant extends Interpolant { - const scope = this; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( buffer ) { + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - try { + } - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - const bufferCopy = buffer.slice( 0 ); + interpolate_( i1, t0, t, t1 ) { - const context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - onLoad( audioBuffer ); + alpha = ( t - t0 ) / ( t1 - t0 ); - } ); + let offset = i1 * stride; - } catch ( e ) { + for ( let end = offset + stride; offset !== end; offset += 4 ) { - if ( onError ) { + Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - onError( e ); + } - } else { + return result; - console.error( e ); + } - } + } - scope.manager.itemError( url ); + /** + * A Track of quaternion keyframe values. + */ + class QuaternionKeyframeTrack extends KeyframeTrack { - } + InterpolantFactoryMethodLinear( result ) { - }, onProgress, onError ); + return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); } } - class HemisphereLightProbe extends LightProbe { - - constructor( skyColor, groundColor, intensity = 1 ) { - - super( undefined, intensity ); - - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); + QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; + // ValueBufferType is inherited + QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); + /** + * A Track that interpolates Strings + */ + class StringKeyframeTrack extends KeyframeTrack {} - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); + StringKeyframeTrack.prototype.ValueTypeName = 'string'; + StringKeyframeTrack.prototype.ValueBufferType = Array; + StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; + StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; + StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); + /** + * A Track of vectored keyframe values. + */ + class VectorKeyframeTrack extends KeyframeTrack {} - } + VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; - } + class AnimationClip { - HemisphereLightProbe.prototype.isHemisphereLightProbe = true; + constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { - class AmbientLightProbe extends LightProbe { + this.name = name; + this.tracks = tracks; + this.duration = duration; + this.blendMode = blendMode; - constructor( color, intensity = 1 ) { + this.uuid = generateUUID(); - super( undefined, intensity ); + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - const color1 = new Color().set( color ); + this.resetDuration(); - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); + } } - } - AmbientLightProbe.prototype.isAmbientLightProbe = true; + static parse( json ) { - class Clock { + const tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - constructor( autoStart = true ) { + for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - this.autoStart = autoStart; + tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; + } - this.running = false; + const clip = new this( json.name, json.duration, tracks, json.blendMode ); + clip.uuid = json.uuid; + + return clip; } - start() { + static toJSON( clip ) { - this.startTime = now(); + const tracks = [], + clipTracks = clip.tracks; - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; + const json = { - } + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid, + 'blendMode': clip.blendMode - stop() { + }; - this.getElapsedTime(); - this.running = false; - this.autoStart = false; + for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - } + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - getElapsedTime() { + } - this.getDelta(); - return this.elapsedTime; + return json; } - getDelta() { - - let diff = 0; + static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { - if ( this.autoStart && ! this.running ) { + const numMorphTargets = morphTargetSequence.length; + const tracks = []; - this.start(); - return 0; + for ( let i = 0; i < numMorphTargets; i ++ ) { - } + let times = []; + let values = []; - if ( this.running ) { + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - const newTime = now(); + values.push( 0, 1, 0 ); - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; + const order = AnimationUtils.getKeyframeOrder( times ); + times = AnimationUtils.sortedArray( times, 1, order ); + values = AnimationUtils.sortedArray( values, 1, order ); - this.elapsedTime += diff; + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( ! noLoop && times[ 0 ] === 0 ) { - } + times.push( numMorphTargets ); + values.push( values[ 0 ] ); - return diff; + } - } + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); - } + } - function now() { + return new this( name, - 1, tracks ); - return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 + } - } + static findByName( objectOrClipArray, name ) { - class Audio extends Object3D { + let clipArray = objectOrClipArray; - constructor( listener ) { + if ( ! Array.isArray( objectOrClipArray ) ) { - super(); + const o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; - this.type = 'Audio'; + } - this.listener = listener; - this.context = listener.context; + for ( let i = 0; i < clipArray.length; i ++ ) { - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); + if ( clipArray[ i ].name === name ) { - this.autoplay = false; + return clipArray[ i ]; - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.source = null; - this.sourceType = 'empty'; + } - this._startedAt = 0; - this._progress = 0; - this._connected = false; + } - this.filters = []; + return null; } - getOutput() { + static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - return this.gain; + const animationToMorphTargets = {}; - } + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + const pattern = /^([\w-]*?)([\d]+)$/; - setNodeSource( audioNode ) { + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); + const morphTarget = morphTargets[ i ]; + const parts = morphTarget.name.match( pattern ); - return this; + if ( parts && parts.length > 1 ) { - } + const name = parts[ 1 ]; - setMediaElementSource( mediaElement ) { + let animationMorphTargets = animationToMorphTargets[ name ]; - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); + if ( ! animationMorphTargets ) { - return this; + animationToMorphTargets[ name ] = animationMorphTargets = []; - } + } - setMediaStreamSource( mediaStream ) { + animationMorphTargets.push( morphTarget ); - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); + } - return this; + } - } + const clips = []; - setBuffer( audioBuffer ) { + for ( const name in animationToMorphTargets ) { - this.buffer = audioBuffer; - this.sourceType = 'buffer'; + clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - if ( this.autoplay ) this.play(); + } - return this; + return clips; } - play( delay = 0 ) { + // parse the animation.hierarchy format + static parseAnimation( animation, bones ) { - if ( this.isPlaying === true ) { + if ( ! animation ) { - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; + console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); + return null; } - if ( this.hasPlaybackControl === false ) { + const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - } + const times = []; + const values = []; - this._startedAt = this.context.currentTime + delay; + AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - const source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - this.isPlaying = true; + destTracks.push( new trackType( trackName, times, values ) ); - this.source = source; + } - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); + } - return this.connect(); + }; - } + const tracks = []; - pause() { + const clipName = animation.name || 'default'; + const fps = animation.fps || 30; + const blendMode = animation.blendMode; - if ( this.hasPlaybackControl === false ) { + // automatic length determination in AnimationClip. + let duration = animation.length || - 1; - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + const hierarchyTracks = animation.hierarchy || []; - } + for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - if ( this.isPlaying === true ) { + const animationKeys = hierarchyTracks[ h ].keys; - // update current progress + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; + // process morph targets + if ( animationKeys[ 0 ].morphTargets ) { - if ( this.loop === true ) { + // figure out all morph targets used in this track + const morphTargetNames = {}; - // ensure _progress does not exceed duration with looped audios + let k; - this._progress = this._progress % ( this.duration || this.buffer.duration ); + for ( k = 0; k < animationKeys.length; k ++ ) { - } + if ( animationKeys[ k ].morphTargets ) { - this.source.stop(); - this.source.onended = null; + for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - this.isPlaying = false; + morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - } + } - return this; + } - } + } - stop() { + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( const morphTargetName in morphTargetNames ) { - if ( this.hasPlaybackControl === false ) { + const times = []; + const values = []; - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - } + const animationKey = animationKeys[ k ]; - this._progress = 0; + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; + } - return this; + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - } + } - connect() { + duration = morphTargetNames.length * fps; - if ( this.filters.length > 0 ) { + } else { - this.source.connect( this.filters[ 0 ] ); + // ...assume skeletal animation - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { + const boneName = '.bones[' + bones[ h ].name + ']'; - this.filters[ i - 1 ].connect( this.filters[ i ] ); + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); + + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); + + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); } - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); + } - } else { + if ( tracks.length === 0 ) { - this.source.connect( this.getOutput() ); + return null; } - this._connected = true; + const clip = new this( clipName, duration, tracks, blendMode ); - return this; + return clip; } - disconnect() { + resetDuration() { - if ( this.filters.length > 0 ) { + const tracks = this.tracks; + let duration = 0; - this.source.disconnect( this.filters[ 0 ] ); + for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { + const track = this.tracks[ i ]; - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); + duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - } + } - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); + this.duration = duration; - } else { + return this; - this.source.disconnect( this.getOutput() ); + } - } + trim() { - this._connected = false; + for ( let i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].trim( 0, this.duration ); + + } return this; } - getFilters() { + validate() { - return this.filters; + let valid = true; - } + for ( let i = 0; i < this.tracks.length; i ++ ) { - setFilters( value ) { + valid = valid && this.tracks[ i ].validate(); - if ( ! value ) value = []; + } - if ( this._connected === true ) { + return valid; - this.disconnect(); - this.filters = value.slice(); - this.connect(); + } - } else { + optimize() { - this.filters = value.slice(); + for ( let i = 0; i < this.tracks.length; i ++ ) { + + this.tracks[ i ].optimize(); } @@ -37689,2443 +37843,2331 @@ } - setDetune( value ) { - - this.detune = value; + clone() { - if ( this.source.detune === undefined ) return; // only set detune when available + const tracks = []; - if ( this.isPlaying === true ) { + for ( let i = 0; i < this.tracks.length; i ++ ) { - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); + tracks.push( this.tracks[ i ].clone() ); } - return this; + return new this.constructor( this.name, this.duration, tracks, this.blendMode ); } - getDetune() { + toJSON() { - return this.detune; + return this.constructor.toJSON( this ); } - getFilter() { + } - return this.getFilters()[ 0 ]; + function getTrackTypeForValueTypeName( typeName ) { - } + switch ( typeName.toLowerCase() ) { - setFilter( filter ) { + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': - return this.setFilters( filter ? [ filter ] : [] ); + return NumberKeyframeTrack; - } + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': - setPlaybackRate( value ) { + return VectorKeyframeTrack; - if ( this.hasPlaybackControl === false ) { + case 'color': - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + return ColorKeyframeTrack; - } + case 'quaternion': - this.playbackRate = value; + return QuaternionKeyframeTrack; - if ( this.isPlaying === true ) { + case 'bool': + case 'boolean': - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); + return BooleanKeyframeTrack; - } + case 'string': - return this; + return StringKeyframeTrack; } - getPlaybackRate() { + throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - return this.playbackRate; + } - } + function parseKeyframeTrack( json ) { - onEnded() { + if ( json.type === undefined ) { - this.isPlaying = false; + throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); } - getLoop() { + const trackType = getTrackTypeForValueTypeName( json.type ); - if ( this.hasPlaybackControl === false ) { + if ( json.times === undefined ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; + const times = [], values = []; - } + AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - return this.loop; + json.times = times; + json.values = values; } - setLoop( value ) { + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - if ( this.hasPlaybackControl === false ) { + return trackType.parse( json ); - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + } else { - } + // by default, we assume a constructor compatible with the base + return new trackType( json.name, json.times, json.values, json.interpolation ); - this.loop = value; + } - if ( this.isPlaying === true ) { + } - this.source.loop = this.loop; + const Cache = { - } + enabled: false, - return this; + files: {}, - } + add: function ( key, file ) { - setLoopStart( value ) { + if ( this.enabled === false ) return; - this.loopStart = value; + // console.log( 'THREE.Cache', 'Adding key:', key ); - return this; + this.files[ key ] = file; - } + }, - setLoopEnd( value ) { + get: function ( key ) { - this.loopEnd = value; + if ( this.enabled === false ) return; - return this; + // console.log( 'THREE.Cache', 'Checking key:', key ); - } + return this.files[ key ]; - getVolume() { + }, - return this.gain.gain.value; + remove: function ( key ) { - } + delete this.files[ key ]; - setVolume( value ) { + }, - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); + clear: function () { - return this; + this.files = {}; } - } - - class PropertyMixer { - - constructor( binding, typeName, valueSize ) { + }; - this.binding = binding; - this.valueSize = valueSize; + class LoadingManager { - let mixFunction, - mixFunctionAdditive, - setIdentity; + constructor( onLoad, onProgress, onError ) { - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results + const scope = this; - switch ( typeName ) { + let isLoading = false; + let itemsLoaded = 0; + let itemsTotal = 0; + let urlModifier = undefined; + const handlers = []; - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; + // Refer to #5689 for the reason why we don't set .onStart + // in the constructor - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - case 'string': - case 'bool': - mixFunction = this._select; + this.itemStart = function ( url ) { - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; + itemsTotal ++; - setIdentity = this._setAdditiveIdentityOther; + if ( isLoading === false ) { - this.buffer = new Array( valueSize * 5 ); - break; + if ( scope.onStart !== undefined ) { - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; + scope.onStart( url, itemsLoaded, itemsTotal ); - this.buffer = new Float64Array( valueSize * 5 ); + } - } + } - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; + isLoading = true; - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + }; - this.useCount = 0; - this.referenceCount = 0; + this.itemEnd = function ( url ) { - } + itemsLoaded ++; - // accumulate data in the 'incoming' region into 'accu' - accumulate( accuIndex, weight ) { + if ( scope.onProgress !== undefined ) { - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place + scope.onProgress( url, itemsLoaded, itemsTotal ); - const buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride; + } - let currentWeight = this.cumulativeWeight; + if ( itemsLoaded === itemsTotal ) { - if ( currentWeight === 0 ) { + isLoading = false; - // accuN := incoming * weight + if ( scope.onLoad !== undefined ) { - for ( let i = 0; i !== stride; ++ i ) { + scope.onLoad(); - buffer[ offset + i ] = buffer[ i ]; + } } - currentWeight = weight; + }; - } else { + this.itemError = function ( url ) { - // accuN := accuN + incoming * weight + if ( scope.onError !== undefined ) { - currentWeight += weight; - const mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); + scope.onError( url ); - } + } - this.cumulativeWeight = currentWeight; + }; - } + this.resolveURL = function ( url ) { - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive( weight ) { + if ( urlModifier ) { - const buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; + return urlModifier( url ); - if ( this.cumulativeWeightAdditive === 0 ) { + } - // add = identity + return url; - this._setIdentity(); + }; - } + this.setURLModifier = function ( transform ) { - // add := add + incoming * weight + urlModifier = transform; - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; + return this; - } + }; - // apply the state of 'accu' to the binding when accus differ - apply( accuIndex ) { + this.addHandler = function ( regex, loader ) { - const stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, + handlers.push( regex, loader ); - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, + return this; - binding = this.binding; + }; - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + this.removeHandler = function ( regex ) { - if ( weight < 1 ) { + const index = handlers.indexOf( regex ); - // accuN := accuN + original * ( 1 - cumulativeWeight ) + if ( index !== - 1 ) { - const originalValueOffset = stride * this._origIndex; + handlers.splice( index, 2 ); - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); + } - } + return this; - if ( weightAdditive > 0 ) { + }; - // accuN := accuN + additive accuN + this.getHandler = function ( file ) { - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); + for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - } + const regex = handlers[ i ]; + const loader = handlers[ i + 1 ]; - for ( let i = stride, e = stride + stride; i !== e; ++ i ) { + if ( regex.global ) regex.lastIndex = 0; // see #17920 - if ( buffer[ i ] !== buffer[ i + stride ] ) { + if ( regex.test( file ) ) { - // value has changed -> update scene graph + return loader; - binding.setValue( buffer, offset ); - break; + } } - } - - } + return null; - // remember the state of the bound property and copy it to both accus - saveOriginalState() { + }; - const binding = this.binding; + } - const buffer = this.buffer, - stride = this.valueSize, + } - originalValueOffset = stride * this._origIndex; + const DefaultLoadingManager = new LoadingManager(); - binding.getValue( buffer, originalValueOffset ); + class Loader { - // accu[0..1] := orig -- initially detect changes against the original - for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { + constructor( manager ) { - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - } + this.crossOrigin = 'anonymous'; + this.withCredentials = false; + this.path = ''; + this.resourcePath = ''; + this.requestHeader = {}; - // Add to identity for additive - this._setIdentity(); + } - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + load( /* url, onLoad, onProgress, onError */ ) {} - } + loadAsync( url, onProgress ) { - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState() { + const scope = this; - const originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); + return new Promise( function ( resolve, reject ) { - } + scope.load( url, resolve, onProgress, reject ); - _setAdditiveIdentityNumeric() { + } ); - const startIndex = this._addIndex * this.valueSize; - const endIndex = startIndex + this.valueSize; + } - for ( let i = startIndex; i < endIndex; i ++ ) { + parse( /* data */ ) {} - this.buffer[ i ] = 0; + setCrossOrigin( crossOrigin ) { - } + this.crossOrigin = crossOrigin; + return this; } - _setAdditiveIdentityQuaternion() { + setWithCredentials( value ) { - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; + this.withCredentials = value; + return this; } - _setAdditiveIdentityOther() { + setPath( path ) { - const startIndex = this._origIndex * this.valueSize; - const targetIndex = this._addIndex * this.valueSize; + this.path = path; + return this; - for ( let i = 0; i < this.valueSize; i ++ ) { + } - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; + setResourcePath( resourcePath ) { - } + this.resourcePath = resourcePath; + return this; } + setRequestHeader( requestHeader ) { - // mix functions + this.requestHeader = requestHeader; + return this; - _select( buffer, dstOffset, srcOffset, t, stride ) { + } - if ( t >= 0.5 ) { + } - for ( let i = 0; i !== stride; ++ i ) { + const loading = {}; - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; + class FileLoader extends Loader { - } + constructor( manager ) { - } + super( manager ); } - _slerp( buffer, dstOffset, srcOffset, t ) { - - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); + load( url, onLoad, onProgress, onError ) { - } + if ( url === undefined ) url = ''; - _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { + if ( this.path !== undefined ) url = this.path + url; - const workOffset = this._workIndex * stride; + url = this.manager.resolveURL( url ); - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); + const cached = Cache.get( url ); - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); + if ( cached !== undefined ) { - } + this.manager.itemStart( url ); - _lerp( buffer, dstOffset, srcOffset, t, stride ) { + setTimeout( () => { - const s = 1 - t; + if ( onLoad ) onLoad( cached ); - for ( let i = 0; i !== stride; ++ i ) { + this.manager.itemEnd( url ); - const j = dstOffset + i; + }, 0 ); - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; + return cached; } - } + // Check if request is duplicate - _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { + if ( loading[ url ] !== undefined ) { - for ( let i = 0; i !== stride; ++ i ) { + loading[ url ].push( { - const j = dstOffset + i; + onLoad: onLoad, + onProgress: onProgress, + onError: onError - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; + } ); - } + return; - } + } - } + // Initialise array for duplicate requests + loading[ url ] = []; - // Characters [].:/ are reserved for track binding syntax. - const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); + loading[ url ].push( { + onLoad: onLoad, + onProgress: onProgress, + onError: onError, + } ); - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; + // create request + const req = new Request( url, { + headers: new Headers( this.requestHeader ), + credentials: this.withCredentials ? 'include' : 'same-origin', + // An abort controller could be added within a future PR + } ); - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); + // record states ( avoid data race ) + const mimeType = this.mimeType; + const responseType = this.responseType; - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); + // start the fetch + fetch( req ) + .then( response => { - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); + if ( response.status === 200 || response.status === 0 ) { - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - const _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); + if ( response.status === 0 ) { - const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; + console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - class Composite { + } - constructor( targetGroup, path, optionalParsedPath ) { + // Workaround: Checking if response.body === undefined for Alipay browser #23548 - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); + if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) { - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); + return response; - } + } - getValue( array, offset ) { + const callbacks = loading[ url ]; + const reader = response.body.getReader(); + const contentLength = response.headers.get( 'Content-Length' ); + const total = contentLength ? parseInt( contentLength ) : 0; + const lengthComputable = total !== 0; + let loaded = 0; - this.bind(); // bind all binding + // periodically read data into the new stream tracking while download progress + const stream = new ReadableStream( { + start( controller ) { - const firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; + readData(); - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); + function readData() { - } + reader.read().then( ( { done, value } ) => { - setValue( array, offset ) { + if ( done ) { - const bindings = this._bindings; + controller.close(); - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + } else { - bindings[ i ].setValue( array, offset ); + loaded += value.byteLength; - } + const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } ); + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - } + const callback = callbacks[ i ]; + if ( callback.onProgress ) callback.onProgress( event ); - bind() { + } - const bindings = this._bindings; + controller.enqueue( value ); + readData(); - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + } - bindings[ i ].bind(); + } ); - } + } - } + } - unbind() { + } ); - const bindings = this._bindings; + return new Response( stream ); - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + } else { - bindings[ i ].unbind(); + throw Error( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}` ); - } + } - } + } ) + .then( response => { - } + switch ( responseType ) { - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. - class PropertyBinding { + case 'arraybuffer': - constructor( rootNode, path, parsedPath ) { + return response.arrayBuffer(); - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); + case 'blob': - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; + return response.blob(); - this.rootNode = rootNode; + case 'document': - // initial state of these methods that calls 'bind' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; + return response.text() + .then( text => { - } + const parser = new DOMParser(); + return parser.parseFromString( text, mimeType ); + } ); - static create( root, path, parsedPath ) { + case 'json': - if ( ! ( root && root.isAnimationObjectGroup ) ) { + return response.json(); - return new PropertyBinding( root, path, parsedPath ); + default: - } else { + if ( mimeType === undefined ) { - return new PropertyBinding.Composite( root, path, parsedPath ); + return response.text(); - } + } else { - } + // sniff encoding + const re = /charset="?([^;"\s]*)"?/i; + const exec = re.exec( mimeType ); + const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; + const decoder = new TextDecoder( label ); + return response.arrayBuffer().then( ab => decoder.decode( ab ) ); - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - static sanitizeNodeName( name ) { + } - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); + } - } + } ) + .then( data => { - static parseTrackName( trackName ) { + // Add to cache only on HTTP success, so that we do not cache + // error response bodies as proper responses to requests. + Cache.add( url, data ); - const matches = _trackRe.exec( trackName ); + const callbacks = loading[ url ]; + delete loading[ url ]; - if ( ! matches ) { + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); + const callback = callbacks[ i ]; + if ( callback.onLoad ) callback.onLoad( data ); - } + } - const results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; + } ) + .catch( err => { - const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); + // Abort errors and other errors are handled the same - if ( lastDot !== undefined && lastDot !== - 1 ) { + const callbacks = loading[ url ]; - const objectName = results.nodeName.substring( lastDot + 1 ); + if ( callbacks === undefined ) { - // Object names must be checked against an allowlist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { + // When onLoad was called and url was deleted in `loading` + this.manager.itemError( url ); + throw err; - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; + } - } + delete loading[ url ]; - } + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - if ( results.propertyName === null || results.propertyName.length === 0 ) { + const callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( err ); - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); + } - } + this.manager.itemError( url ); - return results; + } ) + .finally( () => { - } + this.manager.itemEnd( url ); - static findNode( root, nodeName ) { + } ); - if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { + this.manager.itemStart( url ); - return root; + } - } + setResponseType( value ) { - // search into skeleton bones. - if ( root.skeleton ) { + this.responseType = value; + return this; - const bone = root.skeleton.getBoneByName( nodeName ); + } - if ( bone !== undefined ) { + setMimeType( value ) { - return bone; + this.mimeType = value; + return this; - } + } - } + } - // search into node subtree. - if ( root.children ) { + class ImageLoader extends Loader { - const searchNodeSubtree = function ( children ) { + constructor( manager ) { - for ( let i = 0; i < children.length; i ++ ) { + super( manager ); - const childNode = children[ i ]; + } - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { + load( url, onLoad, onProgress, onError ) { - return childNode; + if ( this.path !== undefined ) url = this.path + url; - } + url = this.manager.resolveURL( url ); - const result = searchNodeSubtree( childNode.children ); + const scope = this; - if ( result ) return result; + const cached = Cache.get( url ); - } + if ( cached !== undefined ) { - return null; + scope.manager.itemStart( url ); - }; + setTimeout( function () { - const subTreeNode = searchNodeSubtree( root.children ); + if ( onLoad ) onLoad( cached ); - if ( subTreeNode ) { + scope.manager.itemEnd( url ); - return subTreeNode; + }, 0 ); - } + return cached; } - return null; + const image = createElementNS( 'img' ); - } + function onImageLoad() { - // these are used to "bind" a nonexistent property - _getValue_unavailable() {} - _setValue_unavailable() {} + removeEventListeners(); - // Getters + Cache.add( url, this ); - _getValue_direct( buffer, offset ) { + if ( onLoad ) onLoad( this ); - buffer[ offset ] = this.node[ this.propertyName ]; + scope.manager.itemEnd( url ); - } + } - _getValue_array( buffer, offset ) { + function onImageError( event ) { - const source = this.resolvedProperty; + removeEventListeners(); - for ( let i = 0, n = source.length; i !== n; ++ i ) { + if ( onError ) onError( event ); - buffer[ offset ++ ] = source[ i ]; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); } - } + function removeEventListeners() { - _getValue_arrayElement( buffer, offset ) { + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; + } - } + image.addEventListener( 'load', onImageLoad, false ); + image.addEventListener( 'error', onImageError, false ); - _getValue_toArray( buffer, offset ) { + if ( url.slice( 0, 5 ) !== 'data:' ) { - this.resolvedProperty.toArray( buffer, offset ); + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - } + } - // Direct + scope.manager.itemStart( url ); - _setValue_direct( buffer, offset ) { + image.src = url; - this.targetObject[ this.propertyName ] = buffer[ offset ]; + return image; } - _setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + } - } + class CubeTextureLoader extends Loader { - _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { + constructor( manager ) { - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + super( manager ); } - // EntireArray - - _setValue_array( buffer, offset ) { - - const dest = this.resolvedProperty; + load( urls, onLoad, onProgress, onError ) { - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + const texture = new CubeTexture(); - dest[ i ] = buffer[ offset ++ ]; + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - } + let loaded = 0; - } + function loadTexture( i ) { - _setValue_array_setNeedsUpdate( buffer, offset ) { + loader.load( urls[ i ], function ( image ) { - const dest = this.resolvedProperty; + texture.images[ i ] = image; - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + loaded ++; - dest[ i ] = buffer[ offset ++ ]; + if ( loaded === 6 ) { - } + texture.needsUpdate = true; - this.targetObject.needsUpdate = true; + if ( onLoad ) onLoad( texture ); - } + } - _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { + }, undefined, onError ); - const dest = this.resolvedProperty; + } - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + for ( let i = 0; i < urls.length; ++ i ) { - dest[ i ] = buffer[ offset ++ ]; + loadTexture( i ); } - this.targetObject.matrixWorldNeedsUpdate = true; + return texture; } - // ArrayElement + } - _setValue_arrayElement( buffer, offset ) { + class TextureLoader extends Loader { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + constructor( manager ) { + + super( manager ); } - _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { + load( url, onLoad, onProgress, onError ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + const texture = new Texture(); - } + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { + loader.load( url, function ( image ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + texture.image = image; + texture.needsUpdate = true; - } + if ( onLoad !== undefined ) { - // HasToFromArray + onLoad( texture ); - _setValue_fromArray( buffer, offset ) { + } - this.resolvedProperty.fromArray( buffer, offset ); + }, onProgress, onError ); + + return texture; } - _setValue_fromArray_setNeedsUpdate( buffer, offset ) { + } - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; + class Light extends Object3D { - } + constructor( color, intensity = 1 ) { - _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { + super(); - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; + this.type = 'Light'; + + this.color = new Color( color ); + this.intensity = intensity; } - _getValue_unbound( targetArray, offset ) { + dispose() { - this.bind(); - this.getValue( targetArray, offset ); + // Empty here in base class; some subclasses override. } - _setValue_unbound( sourceArray, offset ) { + copy( source ) { - this.bind(); - this.setValue( sourceArray, offset ); + super.copy( source ); - } + this.color.copy( source.color ); + this.intensity = source.intensity; - // create getter / setter pair for a property in the scene graph - bind() { + return this; - let targetObject = this.node; - const parsedPath = this.parsedPath; + } - const objectName = parsedPath.objectName; - const propertyName = parsedPath.propertyName; - let propertyIndex = parsedPath.propertyIndex; + toJSON( meta ) { - if ( ! targetObject ) { + const data = super.toJSON( meta ); - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - this.node = targetObject; + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - } + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - // ensure there is a value node - if ( ! targetObject ) { + return data; - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; + } - } + } - if ( objectName ) { + Light.prototype.isLight = true; - let objectIndex = parsedPath.objectIndex; + class HemisphereLight extends Light { - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { + constructor( skyColor, groundColor, intensity ) { - case 'materials': + super( skyColor, intensity ); - if ( ! targetObject.material ) { + this.type = 'HemisphereLight'; - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - } + this.groundColor = new Color( groundColor ); - if ( ! targetObject.material.materials ) { + } - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; + copy( source ) { - } + Light.prototype.copy.call( this, source ); - targetObject = targetObject.material.materials; + this.groundColor.copy( source.groundColor ); - break; + return this; - case 'bones': + } - if ( ! targetObject.skeleton ) { + } - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; + HemisphereLight.prototype.isHemisphereLight = true; - } + const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); + const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); + const _lookTarget$1 = /*@__PURE__*/ new Vector3(); - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. + class LightShadow { - targetObject = targetObject.skeleton.bones; + constructor( camera ) { - // support resolving morphTarget names into indices. - for ( let i = 0; i < targetObject.length; i ++ ) { + this.camera = camera; - if ( targetObject[ i ].name === objectIndex ) { + this.bias = 0; + this.normalBias = 0; + this.radius = 1; + this.blurSamples = 8; - objectIndex = i; - break; + this.mapSize = new Vector2( 512, 512 ); - } + this.map = null; + this.mapPass = null; + this.matrix = new Matrix4(); - } + this.autoUpdate = true; + this.needsUpdate = false; - break; + this._frustum = new Frustum(); + this._frameExtents = new Vector2( 1, 1 ); - default: + this._viewportCount = 1; - if ( targetObject[ objectName ] === undefined ) { + this._viewports = [ - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; + new Vector4( 0, 0, 1, 1 ) - } + ]; - targetObject = targetObject[ objectName ]; + } - } + getViewportCount() { + return this._viewportCount; - if ( objectIndex !== undefined ) { + } - if ( targetObject[ objectIndex ] === undefined ) { + getFrustum() { - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; + return this._frustum; - } + } - targetObject = targetObject[ objectIndex ]; + updateMatrices( light ) { - } + const shadowCamera = this.camera; + const shadowMatrix = this.matrix; - } + _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld$1 ); - // resolve property - const nodeProperty = targetObject[ propertyName ]; + _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget$1 ); + shadowCamera.updateMatrixWorld(); - if ( nodeProperty === undefined ) { + _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); - const nodeName = parsedPath.nodeName; + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - } + } - // determine versioning scheme - let versioning = this.Versioning.None; + getViewport( viewportIndex ) { - this.targetObject = targetObject; + return this._viewports[ viewportIndex ]; - if ( targetObject.needsUpdate !== undefined ) { // material + } - versioning = this.Versioning.NeedsUpdate; + getFrameExtents() { - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + return this._frameExtents; - versioning = this.Versioning.MatrixWorldNeedsUpdate; + } - } + dispose() { - // determine how the property gets bound - let bindingType = this.BindingType.Direct; + if ( this.map ) { - if ( propertyIndex !== undefined ) { + this.map.dispose(); - // access a sub element of the property array (only primitives are supported right now) + } - if ( propertyName === 'morphTargetInfluences' ) { + if ( this.mapPass ) { - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + this.mapPass.dispose(); - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { + } - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; + } - } + copy( source ) { - if ( targetObject.geometry.isBufferGeometry ) { + this.camera = source.camera.clone(); - if ( ! targetObject.geometry.morphAttributes ) { + this.bias = source.bias; + this.radius = source.radius; - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; + this.mapSize.copy( source.mapSize ); - } + return this; - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { + } - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; + clone() { - } + return new this.constructor().copy( this ); + } - } else { + toJSON() { - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; + const object = {}; - } + if ( this.bias !== 0 ) object.bias = this.bias; + if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; + if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - } + object.camera = this.camera.toJSON( false ).object; + delete object.camera.matrix; - bindingType = this.BindingType.ArrayElement; + return object; - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; + } - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { + } - // must use copy for Object3D.Euler/Quaternion + class SpotLightShadow extends LightShadow { - bindingType = this.BindingType.HasFromToArray; + constructor() { - this.resolvedProperty = nodeProperty; + super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - } else if ( Array.isArray( nodeProperty ) ) { + this.focus = 1; - bindingType = this.BindingType.EntireArray; + } - this.resolvedProperty = nodeProperty; + updateMatrices( light ) { - } else { + const camera = this.camera; - this.propertyName = propertyName; + const fov = RAD2DEG * 2 * light.angle * this.focus; + const aspect = this.mapSize.width / this.mapSize.height; + const far = light.distance || camera.far; + + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); } - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; + super.updateMatrices( light ); } - unbind() { + copy( source ) { - this.node = null; + super.copy( source ); - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; + this.focus = source.focus; + + return this; } } - PropertyBinding.Composite = Composite; - - PropertyBinding.prototype.BindingType = { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }; + SpotLightShadow.prototype.isSpotLightShadow = true; - PropertyBinding.prototype.Versioning = { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }; + class SpotLight extends Light { - PropertyBinding.prototype.GetterByBindingType = [ + constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { - PropertyBinding.prototype._getValue_direct, - PropertyBinding.prototype._getValue_array, - PropertyBinding.prototype._getValue_arrayElement, - PropertyBinding.prototype._getValue_toArray, + super( color, intensity ); - ]; + this.type = 'SpotLight'; - PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - [ - // Direct - PropertyBinding.prototype._setValue_direct, - PropertyBinding.prototype._setValue_direct_setNeedsUpdate, - PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, + this.target = new Object3D(); - ], [ + this.distance = distance; + this.angle = angle; + this.penumbra = penumbra; + this.decay = decay; // for physically correct lights, should be 2. - // EntireArray + this.shadow = new SpotLightShadow(); - PropertyBinding.prototype._setValue_array, - PropertyBinding.prototype._setValue_array_setNeedsUpdate, - PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, + } - ], [ + get power() { - // ArrayElement - PropertyBinding.prototype._setValue_arrayElement, - PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, - PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, + // compute the light's luminous power (in lumens) from its intensity (in candela) + // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) + return this.intensity * Math.PI; - ], [ + } - // HasToFromArray - PropertyBinding.prototype._setValue_fromArray, - PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, - PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, + set power( power ) { - ] + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / Math.PI; - ]; + } - class AnimationAction { + dispose() { - constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { + this.shadow.dispose(); - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot; - this.blendMode = blendMode; + } - const tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); + copy( source ) { - const interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; + super.copy( source ); - for ( let i = 0; i !== nTracks; ++ i ) { + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - const interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; + this.target = source.target.clone(); - } + this.shadow = source.shadow.clone(); - this._interpolantSettings = interpolantSettings; + return this; - this._interpolants = interpolants; // bound by the mixer + } - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); + } - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager + SpotLight.prototype.isSpotLight = true; - this._timeScaleInterpolant = null; - this._weightInterpolant = null; + const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); + const _lightPositionWorld = /*@__PURE__*/ new Vector3(); + const _lookTarget = /*@__PURE__*/ new Vector3(); - this.loop = LoopRepeat; - this._loopCount = - 1; + class PointLightShadow extends LightShadow { - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; + constructor() { - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; + super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - this.timeScale = 1; - this._effectiveTimeScale = 1; + this._frameExtents = new Vector2( 4, 2 ); - this.weight = 1; - this._effectiveWeight = 1; + this._viewportCount = 6; - this.repetitions = Infinity; // no. of repetitions when looping + this._viewports = [ + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight + // positive X + new Vector4( 2, 1, 1, 1 ), + // negative X + new Vector4( 0, 1, 1, 1 ), + // positive Z + new Vector4( 3, 1, 1, 1 ), + // negative Z + new Vector4( 1, 1, 1, 1 ), + // positive Y + new Vector4( 3, 0, 1, 1 ), + // negative Y + new Vector4( 1, 0, 1, 1 ) + ]; - this.clampWhenFinished = false;// keep feeding the last frame? + this._cubeDirections = [ + new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), + new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) + ]; - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end + this._cubeUps = [ + new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), + new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) + ]; } - // State & Scheduling + updateMatrices( light, viewportIndex = 0 ) { - play() { + const camera = this.camera; + const shadowMatrix = this.matrix; - this._mixer._activateAction( this ); + const far = light.distance || camera.far; - return this; + if ( far !== camera.far ) { - } + camera.far = far; + camera.updateProjectionMatrix(); - stop() { + } - this._mixer._deactivateAction( this ); + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + camera.position.copy( _lightPositionWorld ); - return this.reset(); + _lookTarget.copy( camera.position ); + _lookTarget.add( this._cubeDirections[ viewportIndex ] ); + camera.up.copy( this._cubeUps[ viewportIndex ] ); + camera.lookAt( _lookTarget ); + camera.updateMatrixWorld(); + + shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix ); } - reset() { + } - this.paused = false; - this.enabled = true; + PointLightShadow.prototype.isPointLightShadow = true; - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling + class PointLight extends Light { - return this.stopFading().stopWarping(); + constructor( color, intensity, distance = 0, decay = 1 ) { - } + super( color, intensity ); - isRunning() { + this.type = 'PointLight'; - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); + this.distance = distance; + this.decay = decay; // for physically correct lights, should be 2. + + this.shadow = new PointLightShadow(); } - // return true when play has been called - isScheduled() { + get power() { - return this._mixer._isActiveAction( this ); + // compute the light's luminous power (in lumens) from its intensity (in candela) + // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) + return this.intensity * 4 * Math.PI; } - startAt( time ) { - - this._startTime = time; + set power( power ) { - return this; + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / ( 4 * Math.PI ); } - setLoop( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; + dispose() { - return this; + this.shadow.dispose(); } - // Weight + copy( source ) { - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight( weight ) { + super.copy( source ); - this.weight = weight; + this.distance = source.distance; + this.decay = source.decay; - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; + this.shadow = source.shadow.clone(); - return this.stopFading(); + return this; } - // return the weight considering fading and .enabled - getEffectiveWeight() { + } - return this._effectiveWeight; + PointLight.prototype.isPointLight = true; - } + class DirectionalLightShadow extends LightShadow { - fadeIn( duration ) { + constructor() { - return this._scheduleFading( duration, 0, 1 ); + super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } - fadeOut( duration ) { + } - return this._scheduleFading( duration, 1, 0 ); + DirectionalLightShadow.prototype.isDirectionalLightShadow = true; - } + class DirectionalLight extends Light { - crossFadeFrom( fadeOutAction, duration, warp ) { + constructor( color, intensity ) { - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); + super( color, intensity ); - if ( warp ) { + this.type = 'DirectionalLight'; - const fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; + this.target = new Object3D(); - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); + this.shadow = new DirectionalLightShadow(); - } + } - return this; + dispose() { + + this.shadow.dispose(); } - crossFadeTo( fadeInAction, duration, warp ) { + copy( source ) { - return fadeInAction.crossFadeFrom( this, duration, warp ); + super.copy( source ); + + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); + + return this; } - stopFading() { + } - const weightInterpolant = this._weightInterpolant; + DirectionalLight.prototype.isDirectionalLight = true; - if ( weightInterpolant !== null ) { + class AmbientLight extends Light { - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); + constructor( color, intensity ) { - } + super( color, intensity ); - return this; + this.type = 'AmbientLight'; } - // Time Scale Control + } - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale( timeScale ) { + AmbientLight.prototype.isAmbientLight = true; - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; + class RectAreaLight extends Light { - return this.stopWarping(); + constructor( color, intensity, width = 10, height = 10 ) { - } + super( color, intensity ); - // return the time scale considering warping and .paused - getEffectiveTimeScale() { + this.type = 'RectAreaLight'; - return this._effectiveTimeScale; + this.width = width; + this.height = height; } - setDuration( duration ) { - - this.timeScale = this._clip.duration / duration; + get power() { - return this.stopWarping(); + // compute the light's luminous power (in lumens) from its intensity (in nits) + return this.intensity * this.width * this.height * Math.PI; } - syncWith( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; + set power( power ) { - return this.stopWarping(); + // set the light's intensity (in nits) from the desired luminous power (in lumens) + this.intensity = power / ( this.width * this.height * Math.PI ); } - halt( duration ) { + copy( source ) { - return this.warp( this._effectiveTimeScale, 0, duration ); + super.copy( source ); - } + this.width = source.width; + this.height = source.height; - warp( startTimeScale, endTimeScale, duration ) { + return this; - const mixer = this._mixer, - now = mixer.time, - timeScale = this.timeScale; + } - let interpolant = this._timeScaleInterpolant; + toJSON( meta ) { - if ( interpolant === null ) { + const data = super.toJSON( meta ); - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; + data.object.width = this.width; + data.object.height = this.height; - } + return data; - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; + } - times[ 0 ] = now; - times[ 1 ] = now + duration; + } - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; + RectAreaLight.prototype.isRectAreaLight = true; - return this; + /** + * Primary reference: + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * Secondary reference: + * https://www.ppsloan.org/publications/StupidSH36.pdf + */ - } + // 3-band SH defined by 9 coefficients - stopWarping() { + class SphericalHarmonics3 { - const timeScaleInterpolant = this._timeScaleInterpolant; + constructor() { - if ( timeScaleInterpolant !== null ) { + this.coefficients = []; - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients.push( new Vector3() ); - return this; + } } - // Object Accessors + set( coefficients ) { - getMixer() { + for ( let i = 0; i < 9; i ++ ) { - return this._mixer; + this.coefficients[ i ].copy( coefficients[ i ] ); + + } + + return this; } - getClip() { + zero() { - return this._clip; + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].set( 0, 0, 0 ); - getRoot() { + } - return this._localRoot || this._mixer._root; + return this; } - // Interna + // get the radiance in the direction of the normal + // target is a Vector3 + getAt( normal, target ) { - _update( time, deltaTime, timeDirection, accuIndex ) { + // normal is assumed to be unit length - // called by the mixer + const x = normal.x, y = normal.y, z = normal.z; - if ( ! this.enabled ) { + const coeff = this.coefficients; - // call ._updateWeight() to update ._effectiveWeight + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - this._updateWeight( time ); - return; + // band 1 + target.addScaledVector( coeff[ 1 ], 0.488603 * y ); + target.addScaledVector( coeff[ 2 ], 0.488603 * z ); + target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - } + // band 2 + target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); + target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); + target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); + target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); + target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - const startTime = this._startTime; + return target; - if ( startTime !== null ) { + } - // check for scheduled start of action + // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal + // target is a Vector3 + // https://graphics.stanford.edu/papers/envmap/envmap.pdf + getIrradianceAt( normal, target ) { - const timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { + // normal is assumed to be unit length - return; // yet to come / don't decide when delta = 0 + const x = normal.x, y = normal.y, z = normal.z; - } + const coeff = this.coefficients; - // start + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; + // band 1 + target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 + target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); + target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - } + // band 2 + target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 + target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); + target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 + target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); + target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - // apply time scale and advance time + return target; - deltaTime *= this._updateTimeScale( time ); - const clipTime = this._updateTime( deltaTime ); + } - // note: _updateTime may disable the action resulting in - // an effective weight of 0 + add( sh ) { - const weight = this._updateWeight( time ); + for ( let i = 0; i < 9; i ++ ) { - if ( weight > 0 ) { + this.coefficients[ i ].add( sh.coefficients[ i ] ); - const interpolants = this._interpolants; - const propertyMixers = this._propertyBindings; + } - switch ( this.blendMode ) { + return this; - case AdditiveAnimationBlendMode: + } - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { + addScaledSH( sh, s ) { - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - break; + } - case NormalAnimationBlendMode: - default: + return this; - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { + } - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); + scale( s ) { - } + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].multiplyScalar( s ); } - } - - _updateWeight( time ) { - - let weight = 0; + return this; - if ( this.enabled ) { + } - weight = this.weight; - const interpolant = this._weightInterpolant; + lerp( sh, alpha ) { - if ( interpolant !== null ) { + for ( let i = 0; i < 9; i ++ ) { - const interpolantValue = interpolant.evaluate( time )[ 0 ]; + this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - weight *= interpolantValue; + } - if ( time > interpolant.parameterPositions[ 1 ] ) { + return this; - this.stopFading(); + } - if ( interpolantValue === 0 ) { + equals( sh ) { - // faded out, disable - this.enabled = false; + for ( let i = 0; i < 9; i ++ ) { - } + if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - } + return false; } } - this._effectiveWeight = weight; - return weight; + return true; } - _updateTimeScale( time ) { + copy( sh ) { - let timeScale = 0; + return this.set( sh.coefficients ); - if ( ! this.paused ) { + } - timeScale = this.timeScale; + clone() { - const interpolant = this._timeScaleInterpolant; + return new this.constructor().copy( this ); - if ( interpolant !== null ) { + } - const interpolantValue = interpolant.evaluate( time )[ 0 ]; + fromArray( array, offset = 0 ) { - timeScale *= interpolantValue; + const coefficients = this.coefficients; - if ( time > interpolant.parameterPositions[ 1 ] ) { + for ( let i = 0; i < 9; i ++ ) { - this.stopWarping(); + coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - if ( timeScale === 0 ) { + } - // motion has halted, pause - this.paused = true; + return this; - } else { + } - // warp done - apply final time scale - this.timeScale = timeScale; + toArray( array = [], offset = 0 ) { - } + const coefficients = this.coefficients; - } + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } - this._effectiveTimeScale = timeScale; - return timeScale; + return array; } - _updateTime( deltaTime ) { + // evaluate the basis functions + // shBasis is an Array[ 9 ] + static getBasisAt( normal, shBasis ) { - const duration = this._clip.duration; - const loop = this.loop; + // normal is assumed to be unit length - let time = this.time + deltaTime; - let loopCount = this._loopCount; + const x = normal.x, y = normal.y, z = normal.z; - const pingPong = ( loop === LoopPingPong ); + // band 0 + shBasis[ 0 ] = 0.282095; - if ( deltaTime === 0 ) { + // band 1 + shBasis[ 1 ] = 0.488603 * y; + shBasis[ 2 ] = 0.488603 * z; + shBasis[ 3 ] = 0.488603 * x; - if ( loopCount === - 1 ) return time; + // band 2 + shBasis[ 4 ] = 1.092548 * x * y; + shBasis[ 5 ] = 1.092548 * y * z; + shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); + shBasis[ 7 ] = 1.092548 * x * z; + shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; + } - } + } - if ( loop === LoopOnce ) { + SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; - if ( loopCount === - 1 ) { + class LightProbe extends Light { - // just started + constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { - this._loopCount = 0; - this._setEndings( true, true, false ); + super( undefined, intensity ); - } + this.sh = sh; - handle_stop: { + } - if ( time >= duration ) { + copy( source ) { - time = duration; + super.copy( source ); - } else if ( time < 0 ) { + this.sh.copy( source.sh ); - time = 0; + return this; - } else { + } - this.time = time; + fromJSON( json ) { - break handle_stop; + this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); + this.sh.fromArray( json.sh ); - } + return this; - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + } - this.time = time; + toJSON( meta ) { - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); + const data = super.toJSON( meta ); - } + data.object.sh = this.sh.toArray(); - } else { // repetitive Repeat or PingPong + return data; - if ( loopCount === - 1 ) { + } - // just started + } - if ( deltaTime >= 0 ) { + LightProbe.prototype.isLightProbe = true; - loopCount = 0; + class LoaderUtils { - this._setEndings( true, this.repetitions === 0, pingPong ); + static decodeText( array ) { - } else { + if ( typeof TextDecoder !== 'undefined' ) { - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 + return new TextDecoder().decode( array ); - this._setEndings( this.repetitions === 0, true, pingPong ); + } - } + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. - } + let s = ''; - if ( time >= duration || time < 0 ) { + for ( let i = 0, il = array.length; i < il; i ++ ) { - // wrap around + // Implicitly assumes little-endian. + s += String.fromCharCode( array[ i ] ); - const loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; + } - loopCount += Math.abs( loopDelta ); + try { - const pending = this.repetitions - loopCount; + // merges multi-byte utf-8 characters. - if ( pending <= 0 ) { + return decodeURIComponent( escape( s ) ); - // have to stop (switch state, clamp time, fire event) + } catch ( e ) { // see #16358 - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + return s; - time = deltaTime > 0 ? duration : 0; + } - this.time = time; + } - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); + static extractUrlBase( url ) { - } else { + const index = url.lastIndexOf( '/' ); - // keep running + if ( index === - 1 ) return './'; - if ( pending === 1 ) { + return url.slice( 0, index + 1 ); - // entering the last round + } - const atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); + static resolveURL( url, path ) { - } else { + // Invalid URL + if ( typeof url !== 'string' || url === '' ) return ''; - this._setEndings( false, false, pingPong ); + // Host Relative URL + if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - } + path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - this._loopCount = loopCount; + } - this.time = time; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) return url; - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) return url; - } + // Blob URL + if ( /^blob:.*$/i.test( url ) ) return url; - } else { + // Relative URL + return path + url; - this.time = time; + } - } + } - if ( pingPong && ( loopCount & 1 ) === 1 ) { + class InstancedBufferGeometry extends BufferGeometry { - // invert time for the "pong round" + constructor() { - return duration - time; + super(); - } + this.type = 'InstancedBufferGeometry'; + this.instanceCount = Infinity; - } + } - return time; + copy( source ) { - } + super.copy( source ); - _setEndings( atStart, atEnd, pingPong ) { + this.instanceCount = source.instanceCount; - const settings = this._interpolantSettings; + return this; - if ( pingPong ) { + } - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; + clone() { - } else { + return new this.constructor().copy( this ); - // assuming for LoopOnce atStart == atEnd == true + } - if ( atStart ) { + toJSON() { - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; + const data = super.toJSON( this ); - } else { + data.instanceCount = this.instanceCount; - settings.endingStart = WrapAroundEnding; + data.isInstancedBufferGeometry = true; - } + return data; - if ( atEnd ) { + } - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; + } - } else { + InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; - settings.endingEnd = WrapAroundEnding; + class ImageBitmapLoader extends Loader { - } + constructor( manager ) { - } + super( manager ); - } + if ( typeof createImageBitmap === 'undefined' ) { - _scheduleFading( duration, weightNow, weightThen ) { + console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - const mixer = this._mixer, now = mixer.time; - let interpolant = this._weightInterpolant; + } - if ( interpolant === null ) { + if ( typeof fetch === 'undefined' ) { - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; + console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); } - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; + this.options = { premultiplyAlpha: 'none' }; - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; + } + + setOptions( options ) { + + this.options = options; return this; } - } + load( url, onLoad, onProgress, onError ) { - class AnimationMixer extends EventDispatcher$1 { + if ( url === undefined ) url = ''; - constructor( root ) { + if ( this.path !== undefined ) url = this.path + url; - super(); + url = this.manager.resolveURL( url ); - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - this.time = 0; - this.timeScale = 1.0; + const scope = this; - } + const cached = Cache.get( url ); - _bindAction( action, prototypeAction ) { + if ( cached !== undefined ) { - const root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName; + scope.manager.itemStart( url ); - let bindingsByName = bindingsByRoot[ rootUuid ]; + setTimeout( function () { - if ( bindingsByName === undefined ) { + if ( onLoad ) onLoad( cached ); - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; + scope.manager.itemEnd( url ); - } + }, 0 ); - for ( let i = 0; i !== nTracks; ++ i ) { + return cached; - const track = tracks[ i ], - trackName = track.name; + } - let binding = bindingsByName[ trackName ]; + const fetchOptions = {}; + fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; + fetchOptions.headers = this.requestHeader; - if ( binding !== undefined ) { + fetch( url, fetchOptions ).then( function ( res ) { - bindings[ i ] = binding; + return res.blob(); - } else { + } ).then( function ( blob ) { - binding = bindings[ i ]; + return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); - if ( binding !== undefined ) { + } ).then( function ( imageBitmap ) { - // existing binding, make sure the cache knows + Cache.add( url, imageBitmap ); - if ( binding._cacheIndex === null ) { + if ( onLoad ) onLoad( imageBitmap ); - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + scope.manager.itemEnd( url ); - } + } ).catch( function ( e ) { - continue; + if ( onError ) onError( e ); - } + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - const path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; + } ); - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); + scope.manager.itemStart( url ); - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + } - bindings[ i ] = binding; + } - } + ImageBitmapLoader.prototype.isImageBitmapLoader = true; - interpolants[ i ].resultBuffer = binding.buffer; + let _context; - } + const AudioContext = { - } + getContext: function () { - _activateAction( action ) { + if ( _context === undefined ) { - if ( ! this._isActiveAction( action ) ) { + _context = new ( window.AudioContext || window.webkitAudioContext )(); - if ( action._cacheIndex === null ) { + } - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind + return _context; - const rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; + }, - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); + setContext: function ( value ) { - this._addInactiveAction( action, clipUuid, rootUuid ); + _context = value; - } + } - const bindings = action._propertyBindings; + }; - // increment reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + class AudioLoader extends Loader { - const binding = bindings[ i ]; + constructor( manager ) { - if ( binding.useCount ++ === 0 ) { + super( manager ); - this._lendBinding( binding ); - binding.saveOriginalState(); + } - } + load( url, onLoad, onProgress, onError ) { - } + const scope = this; - this._lendAction( action ); + const loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( buffer ) { - } + try { - } + // Create a copy of the buffer. The `decodeAudioData` method + // detaches the buffer when complete, preventing reuse. + const bufferCopy = buffer.slice( 0 ); - _deactivateAction( action ) { + const context = AudioContext.getContext(); + context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - if ( this._isActiveAction( action ) ) { + onLoad( audioBuffer ); - const bindings = action._propertyBindings; + } ); - // decrement reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + } catch ( e ) { - const binding = bindings[ i ]; + if ( onError ) { - if ( -- binding.useCount === 0 ) { + onError( e ); - binding.restoreOriginalState(); - this._takeBackBinding( binding ); + } else { + + console.error( e ); } - } + scope.manager.itemError( url ); - this._takeBackAction( action ); + } - } + }, onProgress, onError ); } - // Memory manager - - _initMemoryManager() { + } - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; + class HemisphereLightProbe extends LightProbe { - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } + constructor( skyColor, groundColor, intensity = 1 ) { + super( undefined, intensity ); - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; + const color1 = new Color().set( skyColor ); + const color2 = new Color().set( groundColor ); - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > + const sky = new Vector3( color1.r, color1.g, color1.b ); + const ground = new Vector3( color2.r, color2.g, color2.b ); + // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); + const c0 = Math.sqrt( Math.PI ); + const c1 = c0 * Math.sqrt( 0.75 ); - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; + this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); + this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - const scope = this; + } - this.stats = { + } - actions: { - get total() { + HemisphereLightProbe.prototype.isHemisphereLightProbe = true; - return scope._actions.length; + class AmbientLightProbe extends LightProbe { - }, - get inUse() { + constructor( color, intensity = 1 ) { - return scope._nActiveActions; + super( undefined, intensity ); - } - }, - bindings: { - get total() { + const color1 = new Color().set( color ); - return scope._bindings.length; + // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); + this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); - }, - get inUse() { + } - return scope._nActiveBindings; + } - } - }, - controlInterpolants: { - get total() { + AmbientLightProbe.prototype.isAmbientLightProbe = true; - return scope._controlInterpolants.length; + class Clock { - }, - get inUse() { + constructor( autoStart = true ) { - return scope._nActiveControlInterpolants; + this.autoStart = autoStart; - } - } + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; - }; + this.running = false; } - // Memory management for AnimationAction objects + start() { - _isActiveAction( action ) { + this.startTime = now(); - const index = action._cacheIndex; - return index !== null && index < this._nActiveActions; + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; } - _addInactiveAction( action, clipUuid, rootUuid ) { - - const actions = this._actions, - actionsByClip = this._actionsByClip; - - let actionsForClip = actionsByClip[ clipUuid ]; + stop() { - if ( actionsForClip === undefined ) { + this.getElapsedTime(); + this.running = false; + this.autoStart = false; - actionsForClip = { + } - knownActions: [ action ], - actionByRoot: {} + getElapsedTime() { - }; + this.getDelta(); + return this.elapsedTime; - action._byClipCacheIndex = 0; + } - actionsByClip[ clipUuid ] = actionsForClip; + getDelta() { - } else { + let diff = 0; - const knownActions = actionsForClip.knownActions; + if ( this.autoStart && ! this.running ) { - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); + this.start(); + return 0; } - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; + if ( this.running ) { - } + const newTime = now(); - _removeInactiveAction( action ) { + diff = ( newTime - this.oldTime ) / 1000; + this.oldTime = newTime; - const actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; + this.elapsedTime += diff; - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); + } - action._cacheIndex = null; + return diff; + } - const clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, + } - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], + function now() { - byClipCacheIndex = action._byClipCacheIndex; + return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); + } - action._byClipCacheIndex = null; + class Audio extends Object3D { + constructor( listener ) { - const actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; + super(); - delete actionByRoot[ rootUuid ]; + this.type = 'Audio'; - if ( knownActionsForClip.length === 0 ) { + this.listener = listener; + this.context = listener.context; - delete actionsByClip[ clipUuid ]; + this.gain = this.context.createGain(); + this.gain.connect( listener.getInput() ); - } + this.autoplay = false; - this._removeInactiveBindingsForAction( action ); + this.buffer = null; + this.detune = 0; + this.loop = false; + this.loopStart = 0; + this.loopEnd = 0; + this.offset = 0; + this.duration = undefined; + this.playbackRate = 1; + this.isPlaying = false; + this.hasPlaybackControl = true; + this.source = null; + this.sourceType = 'empty'; - } + this._startedAt = 0; + this._progress = 0; + this._connected = false; - _removeInactiveBindingsForAction( action ) { + this.filters = []; - const bindings = action._propertyBindings; + } - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + getOutput() { - const binding = bindings[ i ]; + return this.gain; - if ( -- binding.referenceCount === 0 ) { + } - this._removeInactiveBinding( binding ); + setNodeSource( audioNode ) { - } + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); - } + return this; } - _lendAction( action ) { + setMediaElementSource( mediaElement ) { - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s + this.hasPlaybackControl = false; + this.sourceType = 'mediaNode'; + this.source = this.context.createMediaElementSource( mediaElement ); + this.connect(); - const actions = this._actions, - prevIndex = action._cacheIndex, + return this; - lastActiveIndex = this._nActiveActions ++, + } - firstInactiveAction = actions[ lastActiveIndex ]; + setMediaStreamSource( mediaStream ) { - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; + this.hasPlaybackControl = false; + this.sourceType = 'mediaStreamNode'; + this.source = this.context.createMediaStreamSource( mediaStream ); + this.connect(); - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; + return this; } - _takeBackAction( action ) { - - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a - - const actions = this._actions, - prevIndex = action._cacheIndex, - - firstInactiveIndex = -- this._nActiveActions, + setBuffer( audioBuffer ) { - lastActiveAction = actions[ firstInactiveIndex ]; + this.buffer = audioBuffer; + this.sourceType = 'buffer'; - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; + if ( this.autoplay ) this.play(); - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; + return this; } - // Memory management for PropertyMixer objects + play( delay = 0 ) { - _addInactiveBinding( binding, rootUuid, trackName ) { + if ( this.isPlaying === true ) { - const bindingsByRoot = this._bindingsByRootAndName, - bindings = this._bindings; + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; - let bindingByName = bindingsByRoot[ rootUuid ]; + } - if ( bindingByName === undefined ) { + if ( this.hasPlaybackControl === false ) { - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - bindingByName[ trackName ] = binding; + this._startedAt = this.context.currentTime + delay; - binding._cacheIndex = bindings.length; - bindings.push( binding ); + const source = this.context.createBufferSource(); + source.buffer = this.buffer; + source.loop = this.loop; + source.loopStart = this.loopStart; + source.loopEnd = this.loopEnd; + source.onended = this.onEnded.bind( this ); + source.start( this._startedAt, this._progress + this.offset, this.duration ); - } + this.isPlaying = true; - _removeInactiveBinding( binding ) { + this.source = source; - const bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], + this.setDetune( this.detune ); + this.setPlaybackRate( this.playbackRate ); - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; + return this.connect(); - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); + } - delete bindingByName[ trackName ]; + pause() { - if ( Object.keys( bindingByName ).length === 0 ) { + if ( this.hasPlaybackControl === false ) { - delete bindingsByRoot[ rootUuid ]; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - } - - _lendBinding( binding ) { - - const bindings = this._bindings, - prevIndex = binding._cacheIndex, - - lastActiveIndex = this._nActiveBindings ++, + if ( this.isPlaying === true ) { - firstInactiveBinding = bindings[ lastActiveIndex ]; + // update current progress - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; + this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; + if ( this.loop === true ) { - } + // ensure _progress does not exceed duration with looped audios - _takeBackBinding( binding ) { + this._progress = this._progress % ( this.duration || this.buffer.duration ); - const bindings = this._bindings, - prevIndex = binding._cacheIndex, + } - firstInactiveIndex = -- this._nActiveBindings, + this.source.stop(); + this.source.onended = null; - lastActiveBinding = bindings[ firstInactiveIndex ]; + this.isPlaying = false; - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; + } - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; + return this; } + stop() { - // Memory management of Interpolants for weight and time scale + if ( this.hasPlaybackControl === false ) { - _lendControlInterpolant() { - - const interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++; - - let interpolant = interpolants[ lastActiveIndex ]; - - if ( interpolant === undefined ) { - - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); - - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - return interpolant; - - } - - _takeBackControlInterpolant( interpolant ) { - - const interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, + this._progress = 0; - firstInactiveIndex = -- this._nActiveControlInterpolants, + this.source.stop(); + this.source.onended = null; + this.isPlaying = false; - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; + return this; - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; + } - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; + connect() { - } + if ( this.filters.length > 0 ) { - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction( clip, optionalRoot, blendMode ) { + this.source.connect( this.filters[ 0 ] ); - const root = optionalRoot || this._root, - rootUuid = root.uuid; + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; + this.filters[ i - 1 ].connect( this.filters[ i ] ); - const clipUuid = clipObject !== null ? clipObject.uuid : clip; + } - const actionsForClip = this._actionsByClip[ clipUuid ]; - let prototypeAction = null; + this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - if ( blendMode === undefined ) { + } else { - if ( clipObject !== null ) { + this.source.connect( this.getOutput() ); - blendMode = clipObject.blendMode; + } - } else { + this._connected = true; - blendMode = NormalAnimationBlendMode; + return this; - } + } - } + disconnect() { - if ( actionsForClip !== undefined ) { + if ( this.filters.length > 0 ) { - const existingAction = actionsForClip.actionByRoot[ rootUuid ]; + this.source.disconnect( this.filters[ 0 ] ); - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - return existingAction; + this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; + this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; + } else { + + this.source.disconnect( this.getOutput() ); } - // clip must be known when specified via string - if ( clipObject === null ) return null; + this._connected = false; - // allocate all resources required to run it - const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); + return this; - this._bindAction( newAction, prototypeAction ); + } - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); + getFilters() { - return newAction; + return this.filters; } - // get an existing action - existingAction( clip, optionalRoot ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid, + setFilters( value ) { - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, + if ( ! value ) value = []; - clipUuid = clipObject ? clipObject.uuid : clip, + if ( this._connected === true ) { - actionsForClip = this._actionsByClip[ clipUuid ]; + this.disconnect(); + this.filters = value.slice(); + this.connect(); - if ( actionsForClip !== undefined ) { + } else { - return actionsForClip.actionByRoot[ rootUuid ] || null; + this.filters = value.slice(); } - return null; + return this; } - // deactivates all previously scheduled actions - stopAllAction() { + setDetune( value ) { - const actions = this._actions, - nActions = this._nActiveActions; + this.detune = value; - for ( let i = nActions - 1; i >= 0; -- i ) { + if ( this.source.detune === undefined ) return; // only set detune when available - actions[ i ].stop(); + if ( this.isPlaying === true ) { + + this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); } @@ -40133,37 +40175,38 @@ } - // advance the time and update apply the animation - update( deltaTime ) { + getDetune() { - deltaTime *= this.timeScale; + return this.detune; - const actions = this._actions, - nActions = this._nActiveActions, + } - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), + getFilter() { - accuIndex = this._accuIndex ^= 1; + return this.getFilters()[ 0 ]; - // run active actions + } - for ( let i = 0; i !== nActions; ++ i ) { + setFilter( filter ) { - const action = actions[ i ]; + return this.setFilters( filter ? [ filter ] : [] ); - action._update( time, deltaTime, timeDirection, accuIndex ); + } - } + setPlaybackRate( value ) { - // update scene graph + if ( this.hasPlaybackControl === false ) { - const bindings = this._bindings, - nBindings = this._nActiveBindings; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - for ( let i = 0; i !== nBindings; ++ i ) { + } - bindings[ i ].apply( accuIndex ); + this.playbackRate = value; + + if ( this.isPlaying === true ) { + + this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); } @@ -40171,2676 +40214,2543 @@ } - // Allows you to seek to a specific time in an animation. - setTime( timeInSeconds ) { - - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( let i = 0; i < this._actions.length; i ++ ) { - - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - - } + getPlaybackRate() { - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. + return this.playbackRate; } - // return this mixer's root target object - getRoot() { + onEnded() { - return this._root; + this.isPlaying = false; } - // free all resources specific to a particular clip - uncacheClip( clip ) { - - const actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; + getLoop() { - if ( actionsForClip !== undefined ) { + if ( this.hasPlaybackControl === false ) { - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return false; - const actionsToRemove = actionsForClip.knownActions; + } - for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { + return this.loop; - const action = actionsToRemove[ i ]; + } - this._deactivateAction( action ); + setLoop( value ) { - const cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; + if ( this.hasPlaybackControl === false ) { - action._cacheIndex = null; - action._byClipCacheIndex = null; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); + } - this._removeInactiveBindingsForAction( action ); + this.loop = value; - } + if ( this.isPlaying === true ) { - delete actionsByClip[ clipUuid ]; + this.source.loop = this.loop; } + return this; + } - // free all resources specific to a particular root target object - uncacheRoot( root ) { + setLoopStart( value ) { - const rootUuid = root.uuid, - actionsByClip = this._actionsByClip; + this.loopStart = value; - for ( const clipUuid in actionsByClip ) { + return this; - const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; + } - if ( action !== undefined ) { + setLoopEnd( value ) { - this._deactivateAction( action ); - this._removeInactiveAction( action ); + this.loopEnd = value; - } + return this; - } + } - const bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; + getVolume() { - if ( bindingByName !== undefined ) { + return this.gain.gain.value; - for ( const trackName in bindingByName ) { + } - const binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); + setVolume( value ) { - } + this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - } + return this; } - // remove a targeted clip from the cache - uncacheAction( clip, optionalRoot ) { - - const action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); + } - } + class PropertyMixer { - } + constructor( binding, typeName, valueSize ) { - } + this.binding = binding; + this.valueSize = valueSize; - AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); + let mixFunction, + mixFunctionAdditive, + setIdentity; - class InstancedInterleavedBuffer extends InterleavedBuffer { + // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + // + // 'add' is used for additive cumulative results + // + // 'work' is optional and is only present for quaternion types. It is used + // to store intermediate quaternion multiplication results - constructor( array, stride, meshPerAttribute = 1 ) { + switch ( typeName ) { - super( array, stride ); + case 'quaternion': + mixFunction = this._slerp; + mixFunctionAdditive = this._slerpAdditive; + setIdentity = this._setAdditiveIdentityQuaternion; - this.meshPerAttribute = meshPerAttribute || 1; + this.buffer = new Float64Array( valueSize * 6 ); + this._workIndex = 5; + break; - } + case 'string': + case 'bool': + mixFunction = this._select; - copy( source ) { + // Use the regular mix function and for additive on these types, + // additive is not relevant for non-numeric types + mixFunctionAdditive = this._select; - super.copy( source ); + setIdentity = this._setAdditiveIdentityOther; - this.meshPerAttribute = source.meshPerAttribute; + this.buffer = new Array( valueSize * 5 ); + break; - return this; + default: + mixFunction = this._lerp; + mixFunctionAdditive = this._lerpAdditive; + setIdentity = this._setAdditiveIdentityNumeric; - } + this.buffer = new Float64Array( valueSize * 5 ); - clone( data ) { + } - const ib = super.clone( data ); + this._mixBufferRegion = mixFunction; + this._mixBufferRegionAdditive = mixFunctionAdditive; + this._setIdentity = setIdentity; + this._origIndex = 3; + this._addIndex = 4; - ib.meshPerAttribute = this.meshPerAttribute; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - return ib; + this.useCount = 0; + this.referenceCount = 0; } - toJSON( data ) { - - const json = super.toJSON( data ); + // accumulate data in the 'incoming' region into 'accu' + accumulate( accuIndex, weight ) { - json.isInstancedInterleavedBuffer = true; - json.meshPerAttribute = this.meshPerAttribute; + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place - return json; + const buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride; - } + let currentWeight = this.cumulativeWeight; - } + if ( currentWeight === 0 ) { - InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; + // accuN := incoming * weight - class Raycaster { + for ( let i = 0; i !== stride; ++ i ) { - constructor( origin, direction, near = 0, far = Infinity ) { + buffer[ offset + i ] = buffer[ i ]; - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) + } - this.near = near; - this.far = far; - this.camera = null; - this.layers = new Layers(); + currentWeight = weight; - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; + } else { - } + // accuN := accuN + incoming * weight - set( origin, direction ) { + currentWeight += weight; + const mix = weight / currentWeight; + this._mixBufferRegion( buffer, offset, 0, mix, stride ); - // direction is assumed to be normalized (for accurate distance calculations) + } - this.ray.set( origin, direction ); + this.cumulativeWeight = currentWeight; } - setFromCamera( coords, camera ) { + // accumulate data in the 'incoming' region into 'add' + accumulateAdditive( weight ) { - if ( camera && camera.isPerspectiveCamera ) { + const buffer = this.buffer, + stride = this.valueSize, + offset = stride * this._addIndex; - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; + if ( this.cumulativeWeightAdditive === 0 ) { - } else if ( camera && camera.isOrthographicCamera ) { + // add = identity - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; + this._setIdentity(); - } else { + } - console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); + // add := add + incoming * weight - } + this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); + this.cumulativeWeightAdditive += weight; } - intersectObject( object, recursive = false, intersects = [] ) { + // apply the state of 'accu' to the binding when accus differ + apply( accuIndex ) { - intersectObject( object, this, intersects, recursive ); + const stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, - intersects.sort( ascSort ); + weight = this.cumulativeWeight, + weightAdditive = this.cumulativeWeightAdditive, - return intersects; + binding = this.binding; - } + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - intersectObjects( objects, recursive = false, intersects = [] ) { + if ( weight < 1 ) { - for ( let i = 0, l = objects.length; i < l; i ++ ) { + // accuN := accuN + original * ( 1 - cumulativeWeight ) - intersectObject( objects[ i ], this, intersects, recursive ); + const originalValueOffset = stride * this._origIndex; + + this._mixBufferRegion( + buffer, offset, originalValueOffset, 1 - weight, stride ); } - intersects.sort( ascSort ); + if ( weightAdditive > 0 ) { - return intersects; + // accuN := accuN + additive accuN - } + this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - } + } - function ascSort( a, b ) { + for ( let i = stride, e = stride + stride; i !== e; ++ i ) { - return a.distance - b.distance; + if ( buffer[ i ] !== buffer[ i + stride ] ) { - } + // value has changed -> update scene graph - function intersectObject( object, raycaster, intersects, recursive ) { + binding.setValue( buffer, offset ); + break; - if ( object.layers.test( raycaster.layers ) ) { + } - object.raycast( raycaster, intersects ); + } } - if ( recursive === true ) { + // remember the state of the bound property and copy it to both accus + saveOriginalState() { - const children = object.children; + const binding = this.binding; - for ( let i = 0, l = children.length; i < l; i ++ ) { + const buffer = this.buffer, + stride = this.valueSize, - intersectObject( children[ i ], raycaster, intersects, true ); + originalValueOffset = stride * this._origIndex; - } + binding.getValue( buffer, originalValueOffset ); - } + // accu[0..1] := orig -- initially detect changes against the original + for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - } + buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - /** - * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system - * - * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. - * The azimuthal angle (theta) is measured from the positive z-axis. - */ + } - class Spherical { + // Add to identity for additive + this._setIdentity(); - constructor( radius = 1, phi = 0, theta = 0 ) { + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - this.radius = radius; - this.phi = phi; // polar angle - this.theta = theta; // azimuthal angle + } - return this; + // apply the state previously taken via 'saveOriginalState' to the binding + restoreOriginalState() { + + const originalValueOffset = this.valueSize * 3; + this.binding.setValue( this.buffer, originalValueOffset ); } - set( radius, phi, theta ) { + _setAdditiveIdentityNumeric() { - this.radius = radius; - this.phi = phi; - this.theta = theta; + const startIndex = this._addIndex * this.valueSize; + const endIndex = startIndex + this.valueSize; - return this; + for ( let i = startIndex; i < endIndex; i ++ ) { - } + this.buffer[ i ] = 0; - copy( other ) { + } - this.radius = other.radius; - this.phi = other.phi; - this.theta = other.theta; + } - return this; + _setAdditiveIdentityQuaternion() { - } + this._setAdditiveIdentityNumeric(); + this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - // restrict phi to be betwee EPS and PI-EPS - makeSafe() { + } - const EPS = 0.000001; - this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); + _setAdditiveIdentityOther() { - return this; + const startIndex = this._origIndex * this.valueSize; + const targetIndex = this._addIndex * this.valueSize; - } + for ( let i = 0; i < this.valueSize; i ++ ) { - setFromVector3( v ) { + this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - return this.setFromCartesianCoords( v.x, v.y, v.z ); + } } - setFromCartesianCoords( x, y, z ) { - this.radius = Math.sqrt( x * x + y * y + z * z ); + // mix functions - if ( this.radius === 0 ) { + _select( buffer, dstOffset, srcOffset, t, stride ) { - this.theta = 0; - this.phi = 0; + if ( t >= 0.5 ) { - } else { + for ( let i = 0; i !== stride; ++ i ) { - this.theta = Math.atan2( x, z ); - this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); + buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - } + } - return this; + } } - clone() { + _slerp( buffer, dstOffset, srcOffset, t ) { - return new this.constructor().copy( this ); + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); } - } + _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - class ImmediateRenderObject extends Object3D { + const workOffset = this._workIndex * stride; - constructor( material ) { + // Store result in intermediate buffer offset + Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - super(); + // Slerp to the intermediate result + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - this.material = material; - this.render = function ( /* renderCallback */ ) {}; + } - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; + _lerp( buffer, dstOffset, srcOffset, t, stride ) { - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; + const s = 1 - t; - this.count = 0; + for ( let i = 0; i !== stride; ++ i ) { - } + const j = dstOffset + i; - } + buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - ImmediateRenderObject.prototype.isImmediateRenderObject = true; + } - const _vector$2 = /*@__PURE__*/ new Vector3(); - const _boneMatrix = /*@__PURE__*/ new Matrix4(); - const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); + } + _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - class SkeletonHelper extends LineSegments { + for ( let i = 0; i !== stride; ++ i ) { - constructor( object ) { + const j = dstOffset + i; - const bones = getBoneList( object ); + buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - const geometry = new BufferGeometry(); + } - const vertices = []; - const colors = []; + } - const color1 = new Color( 0, 0, 1 ); - const color2 = new Color( 0, 1, 0 ); + } - for ( let i = 0; i < bones.length; i ++ ) { + // Characters [].:/ are reserved for track binding syntax. + const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; + const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - const bone = bones[ i ]; + // Attempts to allow node names from any language. ES5's `\w` regexp matches + // only latin characters, and the unicode \p{L} is not yet supported. So + // instead, we exclude reserved characters and match everything else. + const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; + const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - if ( bone.parent && bone.parent.isBone ) { + // Parent directories, delimited by '/' or ':'. Currently unused, but must + // be matched to parse the rest of the track name. + const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); + // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. + const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - } + // Object on target node, and accessor. May not contain reserved + // characters. Accessor may contain any character except closing bracket. + const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - } + // Property and accessor. May not contain reserved characters. Accessor may + // contain any non-bracket characters. + const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + const _trackRe = new RegExp( '' + + '^' + + _directoryRe + + _nodeRe + + _objectRe + + _propertyRe + + '$' + ); - const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); + const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; - super( geometry, material ); + class Composite { - this.type = 'SkeletonHelper'; - this.isSkeletonHelper = true; + constructor( targetGroup, path, optionalParsedPath ) { - this.root = object; - this.bones = bones; + const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); } - updateMatrixWorld( force ) { - - const bones = this.bones; - - const geometry = this.geometry; - const position = geometry.getAttribute( 'position' ); + getValue( array, offset ) { - _matrixWorldInv.copy( this.root.matrixWorld ).invert(); + this.bind(); // bind all binding - for ( let i = 0, j = 0; i < bones.length; i ++ ) { + const firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[ firstValidIndex ]; - const bone = bones[ i ]; + // and only call .getValue on the first + if ( binding !== undefined ) binding.getValue( array, offset ); - if ( bone.parent && bone.parent.isBone ) { + } - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); + setValue( array, offset ) { - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); + const bindings = this._bindings; - j += 2; + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - } + bindings[ i ].setValue( array, offset ); } - geometry.getAttribute( 'position' ).needsUpdate = true; + } - super.updateMatrixWorld( force ); + bind() { - } + const bindings = this._bindings; - } + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + bindings[ i ].bind(); - function getBoneList( object ) { + } - const boneList = []; + } - if ( object && object.isBone ) { + unbind() { - boneList.push( object ); + const bindings = this._bindings; - } + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - for ( let i = 0; i < object.children.length; i ++ ) { + bindings[ i ].unbind(); - boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); + } } - return boneList; - } - class GridHelper extends LineSegments { + // Note: This class uses a State pattern on a per-method basis: + // 'bind' sets 'this.getValue' / 'setValue' and shadows the + // prototype version of these methods with one that represents + // the bound state. When the property is not found, the methods + // become no-ops. + class PropertyBinding { - constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { + constructor( rootNode, path, parsedPath ) { - color1 = new Color( color1 ); - color2 = new Color( color2 ); + this.path = path; + this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - const center = divisions / 2; - const step = size / divisions; - const halfSize = size / 2; + this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; - const vertices = [], colors = []; + this.rootNode = rootNode; - for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { + // initial state of these methods that calls 'bind' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - vertices.push( - halfSize, 0, k, halfSize, 0, k ); - vertices.push( k, 0, - halfSize, k, 0, halfSize ); + } - const color = i === center ? color1 : color2; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; + static create( root, path, parsedPath ) { - } + if ( ! ( root && root.isAnimationObjectGroup ) ) { - const geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + return new PropertyBinding( root, path, parsedPath ); - const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); + } else { - super( geometry, material ); + return new PropertyBinding.Composite( root, path, parsedPath ); - this.type = 'GridHelper'; + } } - } - - const _floatView = new Float32Array( 1 ); - new Int32Array( _floatView.buffer ); + /** + * Replaces spaces with underscores and removes unsupported characters from + * node names, to ensure compatibility with parseTrackName(). + * + * @param {string} name Node name to be sanitized. + * @return {string} + */ + static sanitizeNodeName( name ) { - const backgroundMaterial = new MeshBasicMaterial( { - side: BackSide, - depthWrite: false, - depthTest: false, - } ); - new Mesh( new BoxGeometry(), backgroundMaterial ); + return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - // + } - Curve.create = function ( construct, getPoint ) { + static parseTrackName( trackName ) { - console.log( 'THREE.Curve.create() has been deprecated' ); + const matches = _trackRe.exec( trackName ); - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; + if ( matches === null ) { - return construct; + throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - }; + } - // + const results = { + // directoryName: matches[ 1 ], // (tschw) currently unused + nodeName: matches[ 2 ], + objectName: matches[ 3 ], + objectIndex: matches[ 4 ], + propertyName: matches[ 5 ], // required + propertyIndex: matches[ 6 ] + }; - Path.prototype.fromPoints = function ( points ) { + const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); + if ( lastDot !== undefined && lastDot !== - 1 ) { - }; + const objectName = results.nodeName.substring( lastDot + 1 ); - GridHelper.prototype.setColors = function () { + // Object names must be checked against an allowlist. Otherwise, there + // is no way to parse 'foo.bar.baz': 'baz' must be a property, but + // 'bar' could be the objectName, or part of a nodeName (which can + // include '.' characters). + if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); + results.nodeName = results.nodeName.substring( 0, lastDot ); + results.objectName = objectName; - }; + } - SkeletonHelper.prototype.update = function () { + } - console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); + if ( results.propertyName === null || results.propertyName.length === 0 ) { - }; + throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - // + } - Loader.prototype.extractUrlBase = function ( url ) { + return results; - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); + } - }; + static findNode( root, nodeName ) { - Loader.Handlers = { + if ( nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - add: function ( /* regex, loader */ ) { + return root; - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); + } - }, + // search into skeleton bones. + if ( root.skeleton ) { - get: function ( /* file */ ) { + const bone = root.skeleton.getBoneByName( nodeName ); - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); + if ( bone !== undefined ) { - } + return bone; - }; + } - // + } - Box3.prototype.center = function ( optionalTarget ) { + // search into node subtree. + if ( root.children ) { - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + const searchNodeSubtree = function ( children ) { - }; + for ( let i = 0; i < children.length; i ++ ) { - Box3.prototype.empty = function () { + const childNode = children[ i ]; - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - }; + return childNode; - Box3.prototype.isIntersectionBox = function ( box ) { + } - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + const result = searchNodeSubtree( childNode.children ); - }; + if ( result ) return result; - Box3.prototype.isIntersectionSphere = function ( sphere ) { + } - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); + return null; - }; + }; - Box3.prototype.size = function ( optionalTarget ) { + const subTreeNode = searchNodeSubtree( root.children ); - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); + if ( subTreeNode ) { - }; + return subTreeNode; - // + } - Sphere.prototype.empty = function () { + } - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + return null; - }; + } - // + // these are used to "bind" a nonexistent property + _getValue_unavailable() {} + _setValue_unavailable() {} - Frustum.prototype.setFromMatrix = function ( m ) { + // Getters - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); + _getValue_direct( buffer, offset ) { - }; + buffer[ offset ] = this.targetObject[ this.propertyName ]; - // + } - Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { + _getValue_array( buffer, offset ) { - console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + const source = this.resolvedProperty; - }; + for ( let i = 0, n = source.length; i !== n; ++ i ) { - Matrix3.prototype.multiplyVector3 = function ( vector ) { + buffer[ offset ++ ] = source[ i ]; - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); + } - }; + } - Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { + _getValue_arrayElement( buffer, offset ) { - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - }; + } - Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { + _getValue_toArray( buffer, offset ) { - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); + this.resolvedProperty.toArray( buffer, offset ); - }; + } - Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { + // Direct - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); + _setValue_direct( buffer, offset ) { - }; + this.targetObject[ this.propertyName ] = buffer[ offset ]; - Matrix3.prototype.getInverse = function ( matrix ) { + } - console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + _setValue_direct_setNeedsUpdate( buffer, offset ) { - }; + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - // + } - Matrix4.prototype.extractPosition = function ( m ) { + _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - }; + } - Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { + // EntireArray - console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + _setValue_array( buffer, offset ) { - }; + const dest = this.resolvedProperty; - Matrix4.prototype.getPosition = function () { + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); + dest[ i ] = buffer[ offset ++ ]; - }; + } - Matrix4.prototype.setRotationFromQuaternion = function ( q ) { + } - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); + _setValue_array_setNeedsUpdate( buffer, offset ) { - }; + const dest = this.resolvedProperty; - Matrix4.prototype.multiplyToArray = function () { + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); + dest[ i ] = buffer[ offset ++ ]; - }; + } - Matrix4.prototype.multiplyVector3 = function ( vector ) { + this.targetObject.needsUpdate = true; - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + } - }; + _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - Matrix4.prototype.multiplyVector4 = function ( vector ) { + const dest = this.resolvedProperty; - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - }; + dest[ i ] = buffer[ offset ++ ]; - Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { + } - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); + this.targetObject.matrixWorldNeedsUpdate = true; - }; + } - Matrix4.prototype.rotateAxis = function ( v ) { + // ArrayElement - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); + _setValue_arrayElement( buffer, offset ) { - }; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - Matrix4.prototype.crossVector = function ( vector ) { + } - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - }; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - Matrix4.prototype.translate = function () { + } - console.error( 'THREE.Matrix4: .translate() has been removed.' ); + _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - }; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - Matrix4.prototype.rotateX = function () { + } - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + // HasToFromArray - }; + _setValue_fromArray( buffer, offset ) { - Matrix4.prototype.rotateY = function () { + this.resolvedProperty.fromArray( buffer, offset ); - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + } - }; + _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - Matrix4.prototype.rotateZ = function () { + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + } - }; + _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - Matrix4.prototype.rotateByAxis = function () { + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + } - }; + _getValue_unbound( targetArray, offset ) { - Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { + this.bind(); + this.getValue( targetArray, offset ); - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); + } - }; + _setValue_unbound( sourceArray, offset ) { - Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { + this.bind(); + this.setValue( sourceArray, offset ); - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); + } - }; + // create getter / setter pair for a property in the scene graph + bind() { - Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { + let targetObject = this.node; + const parsedPath = this.parsedPath; - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); + const objectName = parsedPath.objectName; + const propertyName = parsedPath.propertyName; + let propertyIndex = parsedPath.propertyIndex; - }; + if ( ! targetObject ) { - Matrix4.prototype.getInverse = function ( matrix ) { + targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; - console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + this.node = targetObject; - }; + } - // + // set fail state so we can just 'return' on error + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; - Plane.prototype.isIntersectionLine = function ( line ) { + // ensure there is a value node + if ( ! targetObject ) { - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); + console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); + return; - }; + } - // + if ( objectName ) { - Quaternion.prototype.multiplyVector3 = function ( vector ) { + let objectIndex = parsedPath.objectIndex; - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); + // special cases were we need to reach deeper into the hierarchy to get the face materials.... + switch ( objectName ) { - }; + case 'materials': - Quaternion.prototype.inverse = function ( ) { + if ( ! targetObject.material ) { - console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); - return this.invert(); + console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); + return; - }; + } - // + if ( ! targetObject.material.materials ) { - Ray.prototype.isIntersectionBox = function ( box ) { + console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); + return; - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + } - }; + targetObject = targetObject.material.materials; - Ray.prototype.isIntersectionPlane = function ( plane ) { + break; - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); + case 'bones': - }; + if ( ! targetObject.skeleton ) { - Ray.prototype.isIntersectionSphere = function ( sphere ) { + console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); + return; - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); + } - }; + // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. - // + targetObject = targetObject.skeleton.bones; - Triangle.prototype.area = function () { + // support resolving morphTarget names into indices. + for ( let i = 0; i < targetObject.length; i ++ ) { - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); + if ( targetObject[ i ].name === objectIndex ) { - }; + objectIndex = i; + break; - Triangle.prototype.barycoordFromPoint = function ( point, target ) { + } - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); + } - }; + break; - Triangle.prototype.midpoint = function ( target ) { + default: - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); + if ( targetObject[ objectName ] === undefined ) { - }; + console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); + return; - Triangle.prototypenormal = function ( target ) { + } - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); + targetObject = targetObject[ objectName ]; - }; + } - Triangle.prototype.plane = function ( target ) { - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); + if ( objectIndex !== undefined ) { - }; + if ( targetObject[ objectIndex ] === undefined ) { - Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { + console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); + return; - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); + } - }; + targetObject = targetObject[ objectIndex ]; - Triangle.normal = function ( a, b, c, target ) { + } - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); + } - }; + // resolve property + const nodeProperty = targetObject[ propertyName ]; - // + if ( nodeProperty === undefined ) { - Shape.prototype.extractAllPoints = function ( divisions ) { + const nodeName = parsedPath.nodeName; - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); + console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + + '.' + propertyName + ' but it wasn\'t found.', targetObject ); + return; - }; + } - Shape.prototype.extrude = function ( options ) { + // determine versioning scheme + let versioning = this.Versioning.None; - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); + this.targetObject = targetObject; - }; + if ( targetObject.needsUpdate !== undefined ) { // material - Shape.prototype.makeGeometry = function ( options ) { + versioning = this.Versioning.NeedsUpdate; - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - }; + versioning = this.Versioning.MatrixWorldNeedsUpdate; - // + } - Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { + // determine how the property gets bound + let bindingType = this.BindingType.Direct; - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + if ( propertyIndex !== undefined ) { - }; + // access a sub element of the property array (only primitives are supported right now) - Vector2.prototype.distanceToManhattan = function ( v ) { + if ( propertyName === 'morphTargetInfluences' ) { - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - }; + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { - Vector2.prototype.lengthManhattan = function () { + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); + return; - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + } - }; + if ( targetObject.geometry.isBufferGeometry ) { - // + if ( ! targetObject.geometry.morphAttributes ) { - Vector3.prototype.setEulerFromRotationMatrix = function () { + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); + return; - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + } - }; + if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - Vector3.prototype.setEulerFromQuaternion = function () { + propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + } - }; - Vector3.prototype.getPositionFromMatrix = function ( m ) { + } else { - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); + return; - }; + } - Vector3.prototype.getScaleFromMatrix = function ( m ) { + } - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); + bindingType = this.BindingType.ArrayElement; - }; + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; - Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { + } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); + // must use copy for Object3D.Euler/Quaternion - }; + bindingType = this.BindingType.HasFromToArray; - Vector3.prototype.applyProjection = function ( m ) { + this.resolvedProperty = nodeProperty; - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); + } else if ( Array.isArray( nodeProperty ) ) { - }; + bindingType = this.BindingType.EntireArray; - Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { + this.resolvedProperty = nodeProperty; - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + } else { - }; + this.propertyName = propertyName; - Vector3.prototype.distanceToManhattan = function ( v ) { + } - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + // select getter / setter + this.getValue = this.GetterByBindingType[ bindingType ]; + this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - }; + } - Vector3.prototype.lengthManhattan = function () { + unbind() { - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + this.node = null; - }; + // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - // + } - Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { + } - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + PropertyBinding.Composite = Composite; + PropertyBinding.prototype.BindingType = { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 }; - Vector4.prototype.lengthManhattan = function () { - - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); - + PropertyBinding.prototype.Versioning = { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 }; - // + PropertyBinding.prototype.GetterByBindingType = [ - Object3D.prototype.getChildByName = function ( name ) { + PropertyBinding.prototype._getValue_direct, + PropertyBinding.prototype._getValue_array, + PropertyBinding.prototype._getValue_arrayElement, + PropertyBinding.prototype._getValue_toArray, - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); + ]; - }; + PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ - Object3D.prototype.renderDepth = function () { + [ + // Direct + PropertyBinding.prototype._setValue_direct, + PropertyBinding.prototype._setValue_direct_setNeedsUpdate, + PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + ], [ - }; + // EntireArray - Object3D.prototype.translate = function ( distance, axis ) { + PropertyBinding.prototype._setValue_array, + PropertyBinding.prototype._setValue_array_setNeedsUpdate, + PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); + ], [ - }; + // ArrayElement + PropertyBinding.prototype._setValue_arrayElement, + PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, + PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, - Object3D.prototype.getWorldRotation = function () { + ], [ - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); + // HasToFromArray + PropertyBinding.prototype._setValue_fromArray, + PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, + PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, - }; + ] - Object3D.prototype.applyMatrix = function ( matrix ) { + ]; - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + class AnimationAction { - }; + constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { - Object.defineProperties( Object3D.prototype, { + this._mixer = mixer; + this._clip = clip; + this._localRoot = localRoot; + this.blendMode = blendMode; - eulerOrder: { - get: function () { + const tracks = clip.tracks, + nTracks = tracks.length, + interpolants = new Array( nTracks ); - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; + const interpolantSettings = { + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + }; - }, - set: function ( value ) { + for ( let i = 0; i !== nTracks; ++ i ) { - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; + const interpolant = tracks[ i ].createInterpolant( null ); + interpolants[ i ] = interpolant; + interpolant.settings = interpolantSettings; } - }, - useQuaternion: { - get: function () { - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + this._interpolantSettings = interpolantSettings; - }, - set: function () { + this._interpolants = interpolants; // bound by the mixer - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + // inside: PropertyMixer (managed by the mixer) + this._propertyBindings = new Array( nTracks ); - } - } + this._cacheIndex = null; // for the memory manager + this._byClipCacheIndex = null; // for the memory manager - } ); + this._timeScaleInterpolant = null; + this._weightInterpolant = null; - Mesh.prototype.setDrawMode = function () { + this.loop = LoopRepeat; + this._loopCount = - 1; - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); + // global mixer time when the action is to be started + // it's set back to 'null' upon start of the action + this._startTime = null; - }; + // scaled local time of the action + // gets clamped or wrapped to 0..clip.duration according to loop + this.time = 0; - Object.defineProperties( Mesh.prototype, { + this.timeScale = 1; + this._effectiveTimeScale = 1; - drawMode: { - get: function () { + this.weight = 1; + this._effectiveWeight = 1; - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; + this.repetitions = Infinity; // no. of repetitions when looping - }, - set: function () { + this.paused = false; // true -> zero effective time scale + this.enabled = true; // false -> zero effective weight - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); + this.clampWhenFinished = false;// keep feeding the last frame? + + this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate + this.zeroSlopeAtEnd = true;// clips for start, loop and end - } } - } ); + // State & Scheduling - SkinnedMesh.prototype.initBones = function () { + play() { - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); + this._mixer._activateAction( this ); - }; + return this; - // + } - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { + stop() { - console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' + - 'Use .setFocalLength and .filmGauge for a photographic setup.' ); + this._mixer._deactivateAction( this ); - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); + return this.reset(); - }; + } - // + reset() { - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { + this.paused = false; + this.enabled = true; - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + this.time = 0; // restart clip + this._loopCount = - 1;// forget previous loops + this._startTime = null;// forget scheduling - } - }, - shadowCameraFov: { - set: function ( value ) { + return this.stopFading().stopWarping(); - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; + } - } - }, - shadowCameraLeft: { - set: function ( value ) { + isRunning() { - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; + return this.enabled && ! this.paused && this.timeScale !== 0 && + this._startTime === null && this._mixer._isActiveAction( this ); - } - }, - shadowCameraRight: { - set: function ( value ) { + } - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; + // return true when play has been called + isScheduled() { - } - }, - shadowCameraTop: { - set: function ( value ) { + return this._mixer._isActiveAction( this ); - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; + } - } - }, - shadowCameraBottom: { - set: function ( value ) { + startAt( time ) { - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; + this._startTime = time; - } - }, - shadowCameraNear: { - set: function ( value ) { + return this; - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; + } - } - }, - shadowCameraFar: { - set: function ( value ) { + setLoop( mode, repetitions ) { - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; + this.loop = mode; + this.repetitions = repetitions; - } - }, - shadowCameraVisible: { - set: function () { + return this; - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); + } - } - }, - shadowBias: { - set: function ( value ) { + // Weight - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; + // set the weight stopping any scheduled fading + // although .enabled = false yields an effective weight of zero, this + // method does *not* change .enabled, because it would be confusing + setEffectiveWeight( weight ) { - } - }, - shadowDarkness: { - set: function () { + this.weight = weight; - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); + // note: same logic as when updated at runtime + this._effectiveWeight = this.enabled ? weight : 0; - } - }, - shadowMapWidth: { - set: function ( value ) { + return this.stopFading(); - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; + } - } - }, - shadowMapHeight: { - set: function ( value ) { + // return the weight considering fading and .enabled + getEffectiveWeight() { - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; + return this._effectiveWeight; - } } - } ); - // + fadeIn( duration ) { - Object.defineProperties( BufferAttribute.prototype, { + return this._scheduleFading( duration, 0, 1 ); - length: { - get: function () { + } - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; + fadeOut( duration ) { - } - }, - dynamic: { - get: function () { + return this._scheduleFading( duration, 1, 0 ); - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; + } - }, - set: function ( /* value */ ) { + crossFadeFrom( fadeOutAction, duration, warp ) { - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); + fadeOutAction.fadeOut( duration ); + this.fadeIn( duration ); - } - } + if ( warp ) { - } ); + const fadeInDuration = this._clip.duration, + fadeOutDuration = fadeOutAction._clip.duration, - BufferAttribute.prototype.setDynamic = function ( value ) { + startEndRatio = fadeOutDuration / fadeInDuration, + endStartRatio = fadeInDuration / fadeOutDuration; - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; + fadeOutAction.warp( 1.0, startEndRatio, duration ); + this.warp( endStartRatio, 1.0, duration ); - }; + } - BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { + return this; - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); + } - }, + crossFadeTo( fadeInAction, duration, warp ) { - BufferAttribute.prototype.setArray = function ( /* array */ ) { + return fadeInAction.crossFadeFrom( this, duration, warp ); - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); + } - }; + stopFading() { - // + const weightInterpolant = this._weightInterpolant; - BufferGeometry.prototype.addIndex = function ( index ) { + if ( weightInterpolant !== null ) { - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); + this._weightInterpolant = null; + this._mixer._takeBackControlInterpolant( weightInterpolant ); - }; + } - BufferGeometry.prototype.addAttribute = function ( name, attribute ) { + return this; - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); + } - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { + // Time Scale Control - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + // set the time scale stopping any scheduled warping + // although .paused = true yields an effective time scale of zero, this + // method does *not* change .paused, because it would be confusing + setEffectiveTimeScale( timeScale ) { - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + this.timeScale = timeScale; + this._effectiveTimeScale = this.paused ? 0 : timeScale; - } + return this.stopWarping(); - if ( name === 'index' ) { + } - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); + // return the time scale considering warping and .paused + getEffectiveTimeScale() { - return this; + return this._effectiveTimeScale; } - return this.setAttribute( name, attribute ); + setDuration( duration ) { - }; + this.timeScale = this._clip.duration / duration; - BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { + return this.stopWarping(); - if ( indexOffset !== undefined ) { + } - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + syncWith( action ) { - } + this.time = action.time; + this.timeScale = action.timeScale; - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); + return this.stopWarping(); - }; + } - BufferGeometry.prototype.clearDrawCalls = function () { + halt( duration ) { - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); + return this.warp( this._effectiveTimeScale, 0, duration ); - }; + } - BufferGeometry.prototype.computeOffsets = function () { + warp( startTimeScale, endTimeScale, duration ) { - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + const mixer = this._mixer, + now = mixer.time, + timeScale = this.timeScale; - }; + let interpolant = this._timeScaleInterpolant; - BufferGeometry.prototype.removeAttribute = function ( name ) { + if ( interpolant === null ) { - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); + interpolant = mixer._lendControlInterpolant(); + this._timeScaleInterpolant = interpolant; - return this.deleteAttribute( name ); + } - }; + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; - BufferGeometry.prototype.applyMatrix = function ( matrix ) { + times[ 0 ] = now; + times[ 1 ] = now + duration; - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + values[ 0 ] = startTimeScale / timeScale; + values[ 1 ] = endTimeScale / timeScale; - }; + return this; - Object.defineProperties( BufferGeometry.prototype, { + } - drawcalls: { - get: function () { + stopWarping() { - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; + const timeScaleInterpolant = this._timeScaleInterpolant; - } - }, - offsets: { - get: function () { + if ( timeScaleInterpolant !== null ) { - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; + this._timeScaleInterpolant = null; + this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } - } - } ); + return this; - InterleavedBuffer.prototype.setDynamic = function ( value ) { + } - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; + // Object Accessors - }; + getMixer() { - InterleavedBuffer.prototype.setArray = function ( /* array */ ) { + return this._mixer; - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); + } - }; + getClip() { - // + return this._clip; - ExtrudeGeometry.prototype.getArrays = function () { + } - console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); + getRoot() { - }; + return this._localRoot || this._mixer._root; - ExtrudeGeometry.prototype.addShapeList = function () { + } - console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); + // Interna - }; + _update( time, deltaTime, timeDirection, accuIndex ) { - ExtrudeGeometry.prototype.addShape = function () { + // called by the mixer - console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); + if ( ! this.enabled ) { - }; + // call ._updateWeight() to update ._effectiveWeight - // + this._updateWeight( time ); + return; - Scene.prototype.dispose = function () { + } - console.error( 'THREE.Scene: .dispose() has been removed.' ); + const startTime = this._startTime; - }; + if ( startTime !== null ) { - // + // check for scheduled start of action - Object.defineProperties( Material.prototype, { + const timeRunning = ( time - startTime ) * timeDirection; + if ( timeRunning < 0 || timeDirection === 0 ) { - wrapAround: { - get: function () { + return; // yet to come / don't decide when delta = 0 - console.warn( 'THREE.Material: .wrapAround has been removed.' ); + } - }, - set: function () { + // start - console.warn( 'THREE.Material: .wrapAround has been removed.' ); + this._startTime = null; // unschedule + deltaTime = timeDirection * timeRunning; } - }, - overdraw: { - get: function () { + // apply time scale and advance time - console.warn( 'THREE.Material: .overdraw has been removed.' ); + deltaTime *= this._updateTimeScale( time ); + const clipTime = this._updateTime( deltaTime ); - }, - set: function () { + // note: _updateTime may disable the action resulting in + // an effective weight of 0 - console.warn( 'THREE.Material: .overdraw has been removed.' ); + const weight = this._updateWeight( time ); - } - }, + if ( weight > 0 ) { - wrapRGB: { - get: function () { + const interpolants = this._interpolants; + const propertyMixers = this._propertyBindings; - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); + switch ( this.blendMode ) { - } - }, + case AdditiveAnimationBlendMode: - shading: { - get: function () { + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulateAdditive( weight ); - }, - set: function ( value ) { + } - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); + break; - } - }, + case NormalAnimationBlendMode: + default: - stencilMask: { - get: function () { + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulate( accuIndex, weight ); - }, - set: function ( value ) { + } - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; + } } + } - } ); + _updateWeight( time ) { - Object.defineProperties( ShaderMaterial.prototype, { + let weight = 0; - derivatives: { - get: function () { + if ( this.enabled ) { - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; + weight = this.weight; + const interpolant = this._weightInterpolant; - }, - set: function ( value ) { + if ( interpolant !== null ) { - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - } - } + weight *= interpolantValue; - } ); + if ( time > interpolant.parameterPositions[ 1 ] ) { - // + this.stopFading(); - WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { + if ( interpolantValue === 0 ) { - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + // faded out, disable + this.enabled = false; - }; + } - WebGLRenderer.prototype.animate = function ( callback ) { + } - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); + } - }; + } - WebGLRenderer.prototype.getCurrentRenderTarget = function () { + this._effectiveWeight = weight; + return weight; - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); + } - }; + _updateTimeScale( time ) { - WebGLRenderer.prototype.getMaxAnisotropy = function () { + let timeScale = 0; - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); + if ( ! this.paused ) { - }; + timeScale = this.timeScale; - WebGLRenderer.prototype.getPrecision = function () { + const interpolant = this._timeScaleInterpolant; - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; + if ( interpolant !== null ) { - }; + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - WebGLRenderer.prototype.resetGLState = function () { + timeScale *= interpolantValue; - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); + if ( time > interpolant.parameterPositions[ 1 ] ) { - }; + this.stopWarping(); - WebGLRenderer.prototype.supportsFloatTextures = function () { + if ( timeScale === 0 ) { - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); + // motion has halted, pause + this.paused = true; - }; + } else { - WebGLRenderer.prototype.supportsHalfFloatTextures = function () { + // warp done - apply final time scale + this.timeScale = timeScale; - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); + } - }; + } - WebGLRenderer.prototype.supportsStandardDerivatives = function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); + } - }; + this._effectiveTimeScale = timeScale; + return timeScale; - WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); + _updateTime( deltaTime ) { - }; + const duration = this._clip.duration; + const loop = this.loop; - WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { + let time = this.time + deltaTime; + let loopCount = this._loopCount; - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + const pingPong = ( loop === LoopPingPong ); - }; + if ( deltaTime === 0 ) { - WebGLRenderer.prototype.supportsBlendMinMax = function () { + if ( loopCount === - 1 ) return time; - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); + return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; - }; + } - WebGLRenderer.prototype.supportsVertexTextures = function () { + if ( loop === LoopOnce ) { - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; + if ( loopCount === - 1 ) { - }; + // just started - WebGLRenderer.prototype.supportsInstancedArrays = function () { + this._loopCount = 0; + this._setEndings( true, true, false ); - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); + } - }; + handle_stop: { - WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { + if ( time >= duration ) { - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); + time = duration; - }; + } else if ( time < 0 ) { - WebGLRenderer.prototype.initMaterial = function () { + time = 0; - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + } else { - }; + this.time = time; - WebGLRenderer.prototype.addPrePlugin = function () { + break handle_stop; - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + } - }; + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; - WebGLRenderer.prototype.addPostPlugin = function () { + this.time = time; - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime < 0 ? - 1 : 1 + } ); - }; + } - WebGLRenderer.prototype.updateShadowMap = function () { + } else { // repetitive Repeat or PingPong - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + if ( loopCount === - 1 ) { - }; + // just started - WebGLRenderer.prototype.setFaceCulling = function () { + if ( deltaTime >= 0 ) { - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + loopCount = 0; - }; + this._setEndings( true, this.repetitions === 0, pingPong ); - WebGLRenderer.prototype.allocTextureUnit = function () { + } else { - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + // when looping in reverse direction, the initial + // transition through zero counts as a repetition, + // so leave loopCount at -1 - }; + this._setEndings( this.repetitions === 0, true, pingPong ); - WebGLRenderer.prototype.setTexture = function () { + } - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + } - }; + if ( time >= duration || time < 0 ) { - WebGLRenderer.prototype.setTexture2D = function () { + // wrap around - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + const loopDelta = Math.floor( time / duration ); // signed + time -= duration * loopDelta; - }; + loopCount += Math.abs( loopDelta ); - WebGLRenderer.prototype.setTextureCube = function () { + const pending = this.repetitions - loopCount; - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + if ( pending <= 0 ) { - }; - - WebGLRenderer.prototype.getActiveMipMapLevel = function () { + // have to stop (switch state, clamp time, fire event) - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; - }; + time = deltaTime > 0 ? duration : 0; - Object.defineProperties( WebGLRenderer.prototype, { + this.time = time; - shadowMapEnabled: { - get: function () { + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime > 0 ? 1 : - 1 + } ); - return this.shadowMap.enabled; + } else { - }, - set: function ( value ) { + // keep running - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; + if ( pending === 1 ) { - } - }, - shadowMapType: { - get: function () { + // entering the last round - return this.shadowMap.type; + const atStart = deltaTime < 0; + this._setEndings( atStart, ! atStart, pingPong ); - }, - set: function ( value ) { + } else { - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; + this._setEndings( false, false, pingPong ); - } - }, - shadowMapCullFace: { - get: function () { + } - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + this._loopCount = loopCount; - }, - set: function ( /* value */ ) { + this.time = time; - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); + this._mixer.dispatchEvent( { + type: 'loop', action: this, loopDelta: loopDelta + } ); - } - }, - context: { - get: function () { + } - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); + } else { - } - }, - vr: { - get: function () { + this.time = time; - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; + } - } - }, - gammaInput: { - get: function () { + if ( pingPong && ( loopCount & 1 ) === 1 ) { - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; + // invert time for the "pong round" - }, - set: function () { + return duration - time; - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); + } } - }, - gammaOutput: { - get: function () { - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; + return time; - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; + _setEndings( atStart, atEnd, pingPong ) { - } - }, - toneMappingWhitePoint: { - get: function () { + const settings = this._interpolantSettings; - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - return 1.0; + if ( pingPong ) { - }, - set: function () { + settings.endingStart = ZeroSlopeEnding; + settings.endingEnd = ZeroSlopeEnding; - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); + } else { - } - }, + // assuming for LoopOnce atStart == atEnd == true - } ); + if ( atStart ) { - Object.defineProperties( WebGLShadowMap.prototype, { + settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - cullFace: { - get: function () { + } else { - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + settings.endingStart = WrapAroundEnding; - }, - set: function ( /* cullFace */ ) { + } - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); + if ( atEnd ) { - } - }, - renderReverseSided: { - get: function () { + settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; + } else { - }, - set: function () { + settings.endingEnd = WrapAroundEnding; - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); + } } - }, - renderSingleSided: { - get: function () { - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; - - }, - set: function () { - - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - - } } - } ); + _scheduleFading( duration, weightNow, weightThen ) { - // + const mixer = this._mixer, now = mixer.time; + let interpolant = this._weightInterpolant; - Object.defineProperties( WebGLRenderTarget.prototype, { + if ( interpolant === null ) { - wrapS: { - get: function () { + interpolant = mixer._lendControlInterpolant(); + this._weightInterpolant = interpolant; - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; + } - }, - set: function ( value ) { + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; + times[ 0 ] = now; + values[ 0 ] = weightNow; + times[ 1 ] = now + duration; + values[ 1 ] = weightThen; - } - }, - wrapT: { - get: function () { + return this; - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; + } - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; + class AnimationMixer extends EventDispatcher$1 { - } - }, - magFilter: { - get: function () { + constructor( root ) { - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; + super(); - }, - set: function ( value ) { + this._root = root; + this._initMemoryManager(); + this._accuIndex = 0; + this.time = 0; + this.timeScale = 1.0; - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; + } - } - }, - minFilter: { - get: function () { + _bindAction( action, prototypeAction ) { - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; + const root = action._localRoot || this._root, + tracks = action._clip.tracks, + nTracks = tracks.length, + bindings = action._propertyBindings, + interpolants = action._interpolants, + rootUuid = root.uuid, + bindingsByRoot = this._bindingsByRootAndName; - }, - set: function ( value ) { + let bindingsByName = bindingsByRoot[ rootUuid ]; - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; + if ( bindingsByName === undefined ) { + + bindingsByName = {}; + bindingsByRoot[ rootUuid ] = bindingsByName; } - }, - anisotropy: { - get: function () { - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; + for ( let i = 0; i !== nTracks; ++ i ) { - }, - set: function ( value ) { + const track = tracks[ i ], + trackName = track.name; - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; + let binding = bindingsByName[ trackName ]; - } - }, - offset: { - get: function () { + if ( binding !== undefined ) { - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; + ++ binding.referenceCount; + bindings[ i ] = binding; - }, - set: function ( value ) { + } else { - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; + binding = bindings[ i ]; - } - }, - repeat: { - get: function () { + if ( binding !== undefined ) { - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; + // existing binding, make sure the cache knows - }, - set: function ( value ) { + if ( binding._cacheIndex === null ) { - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); - } - }, - format: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; + continue; - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; + const path = prototypeAction && prototypeAction. + _propertyBindings[ i ].binding.parsedPath; - } - }, - type: { - get: function () { + binding = new PropertyMixer( + PropertyBinding.create( root, trackName, path ), + track.ValueTypeName, track.getValueSize() ); - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); - }, - set: function ( value ) { + bindings[ i ] = binding; - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; + } + + interpolants[ i ].resultBuffer = binding.buffer; } - }, - generateMipmaps: { - get: function () { - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; + } - }, - set: function ( value ) { + _activateAction( action ) { - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; + if ( ! this._isActiveAction( action ) ) { - } - } + if ( action._cacheIndex === null ) { - } ); + // this action has been forgotten by the cache, but the user + // appears to be still using it -> rebind - // + const rootUuid = ( action._localRoot || this._root ).uuid, + clipUuid = action._clip.uuid, + actionsForClip = this._actionsByClip[ clipUuid ]; - Audio.prototype.load = function ( file ) { + this._bindAction( action, + actionsForClip && actionsForClip.knownActions[ 0 ] ); - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { + this._addInactiveAction( action, clipUuid, rootUuid ); - scope.setBuffer( buffer ); + } - } ); - return this; + const bindings = action._propertyBindings; - }; + // increment reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - // + const binding = bindings[ i ]; - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { + if ( binding.useCount ++ === 0 ) { - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); + this._lendBinding( binding ); + binding.saveOriginalState(); - }; + } - CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) { + } - console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' ); - return this.renderTarget.clear( renderer, color, depth, stencil ); + this._lendAction( action ); - }; + } - ImageUtils.crossOrigin = undefined; + } - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { + _deactivateAction( action ) { - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); + if ( this._isActiveAction( action ) ) { - const loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + const bindings = action._propertyBindings; - const texture = loader.load( url, onLoad, undefined, onError ); + // decrement reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - if ( mapping ) texture.mapping = mapping; + const binding = bindings[ i ]; - return texture; + if ( -- binding.useCount === 0 ) { - }; + binding.restoreOriginalState(); + this._takeBackBinding( binding ); - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { + } - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); + } - const loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + this._takeBackAction( action ); - const texture = loader.load( urls, onLoad, undefined, onError ); + } - if ( mapping ) texture.mapping = mapping; + } - return texture; + // Memory manager - }; + _initMemoryManager() { - ImageUtils.loadCompressedTexture = function () { + this._actions = []; // 'nActiveActions' followed by inactive ones + this._nActiveActions = 0; - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); + this._actionsByClip = {}; + // inside: + // { + // knownActions: Array< AnimationAction > - used as prototypes + // actionByRoot: AnimationAction - lookup + // } - }; - ImageUtils.loadCompressedTextureCube = function () { + this._bindings = []; // 'nActiveBindings' followed by inactive ones + this._nActiveBindings = 0; - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - }; - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + this._controlInterpolants = []; // same game as above + this._nActiveControlInterpolants = 0; - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ + const scope = this; - } + this.stats = { - if ( typeof window !== 'undefined' ) { + actions: { + get total() { - if ( window.__THREE__ ) { + return scope._actions.length; - console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); + }, + get inUse() { - } else { + return scope._nActiveActions; - window.__THREE__ = REVISION; + } + }, + bindings: { + get total() { - } + return scope._bindings.length; - } + }, + get inUse() { - const _taskCache = new WeakMap(); + return scope._nActiveBindings; - class DRACOLoader extends Loader { + } + }, + controlInterpolants: { + get total() { - constructor( manager ) { + return scope._controlInterpolants.length; - super( manager ); + }, + get inUse() { - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; + return scope._nActiveControlInterpolants; - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; + } + } - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' }; } - setDecoderPath( path ) { + // Memory management for AnimationAction objects - this.decoderPath = path; + _isActiveAction( action ) { - return this; + const index = action._cacheIndex; + return index !== null && index < this._nActiveActions; } - setDecoderConfig( config ) { + _addInactiveAction( action, clipUuid, rootUuid ) { - this.decoderConfig = config; + const actions = this._actions, + actionsByClip = this._actionsByClip; - return this; + let actionsForClip = actionsByClip[ clipUuid ]; - } + if ( actionsForClip === undefined ) { - setWorkerLimit( workerLimit ) { + actionsForClip = { - this.workerLimit = workerLimit; + knownActions: [ action ], + actionByRoot: {} - return this; + }; - } + action._byClipCacheIndex = 0; - load( url, onLoad, onProgress, onError ) { + actionsByClip[ clipUuid ] = actionsForClip; - const loader = new FileLoader( this.manager ); + } else { - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); + const knownActions = actionsForClip.knownActions; - loader.load( url, ( buffer ) => { + action._byClipCacheIndex = knownActions.length; + knownActions.push( action ); - const taskConfig = { - attributeIDs: this.defaultAttributeIDs, - attributeTypes: this.defaultAttributeTypes, - useUniqueIDs: false - }; + } - this.decodeGeometry( buffer, taskConfig ) - .then( onLoad ) - .catch( onError ); + action._cacheIndex = actions.length; + actions.push( action ); - }, onProgress, onError ); + actionsForClip.actionByRoot[ rootUuid ] = action; } - /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) { - - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs - }; + _removeInactiveAction( action ) { - this.decodeGeometry( buffer, taskConfig ).then( callback ); + const actions = this._actions, + lastInactiveAction = actions[ actions.length - 1 ], + cacheIndex = action._cacheIndex; - } + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); - decodeGeometry( buffer, taskConfig ) { + action._cacheIndex = null; - // TODO: For backward-compatibility, support 'attributeTypes' objects containing - // references (rather than names) to typed array constructors. These must be - // serialized before sending them to the worker. - for ( const attribute in taskConfig.attributeTypes ) { - const type = taskConfig.attributeTypes[ attribute ]; + const clipUuid = action._clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ], + knownActionsForClip = actionsForClip.knownActions, - if ( type.BYTES_PER_ELEMENT !== undefined ) { + lastKnownAction = + knownActionsForClip[ knownActionsForClip.length - 1 ], - taskConfig.attributeTypes[ attribute ] = type.name; + byClipCacheIndex = action._byClipCacheIndex; - } + lastKnownAction._byClipCacheIndex = byClipCacheIndex; + knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; + knownActionsForClip.pop(); - } + action._byClipCacheIndex = null; - // - const taskKey = JSON.stringify( taskConfig ); + const actionByRoot = actionsForClip.actionByRoot, + rootUuid = ( action._localRoot || this._root ).uuid; - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache.has( buffer ) ) { + delete actionByRoot[ rootUuid ]; - const cachedTask = _taskCache.get( buffer ); + if ( knownActionsForClip.length === 0 ) { - if ( cachedTask.key === taskKey ) { + delete actionsByClip[ clipUuid ]; - return cachedTask.promise; + } - } else if ( buffer.byteLength === 0 ) { + this._removeInactiveBindingsForAction( action ); - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( + } - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' + _removeInactiveBindingsForAction( action ) { - ); + const bindings = action._propertyBindings; - } + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - } + const binding = bindings[ i ]; - // + if ( -- binding.referenceCount === 0 ) { - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; + this._removeInactiveBinding( binding ); - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { + } - worker = _worker; + } - return new Promise( ( resolve, reject ) => { + } - worker._callbacks[ taskID ] = { resolve, reject }; + _lendAction( action ) { - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); + // [ active actions | inactive actions ] + // [ active actions >| inactive actions ] + // s a + // <-swap-> + // a s - // this.debug(); + const actions = this._actions, + prevIndex = action._cacheIndex, - } ); + lastActiveIndex = this._nActiveActions ++, - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); + firstInactiveAction = actions[ lastActiveIndex ]; - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { + action._cacheIndex = lastActiveIndex; + actions[ lastActiveIndex ] = action; - if ( worker && taskID ) { + firstInactiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = firstInactiveAction; - this._releaseTask( worker, taskID ); + } - // this.debug(); + _takeBackAction( action ) { - } + // [ active actions | inactive actions ] + // [ active actions |< inactive actions ] + // a s + // <-swap-> + // s a - } ); + const actions = this._actions, + prevIndex = action._cacheIndex, - // Cache the task result. - _taskCache.set( buffer, { + firstInactiveIndex = -- this._nActiveActions, - key: taskKey, - promise: geometryPending + lastActiveAction = actions[ firstInactiveIndex ]; - } ); + action._cacheIndex = firstInactiveIndex; + actions[ firstInactiveIndex ] = action; - return geometryPending; + lastActiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = lastActiveAction; } - _createGeometry( geometryData ) { + // Memory management for PropertyMixer objects - const geometry = new BufferGeometry(); + _addInactiveBinding( binding, rootUuid, trackName ) { - if ( geometryData.index ) { + const bindingsByRoot = this._bindingsByRootAndName, + bindings = this._bindings; - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); + let bindingByName = bindingsByRoot[ rootUuid ]; + + if ( bindingByName === undefined ) { + + bindingByName = {}; + bindingsByRoot[ rootUuid ] = bindingByName; } - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { + bindingByName[ trackName ] = binding; - const attribute = geometryData.attributes[ i ]; - const name = attribute.name; - const array = attribute.array; - const itemSize = attribute.itemSize; + binding._cacheIndex = bindings.length; + bindings.push( binding ); - geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); + } - } + _removeInactiveBinding( binding ) { - return geometry; + const bindings = this._bindings, + propBinding = binding.binding, + rootUuid = propBinding.rootNode.uuid, + trackName = propBinding.path, + bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ], - } + lastInactiveBinding = bindings[ bindings.length - 1 ], + cacheIndex = binding._cacheIndex; - _loadLibrary( url, responseType ) { + lastInactiveBinding._cacheIndex = cacheIndex; + bindings[ cacheIndex ] = lastInactiveBinding; + bindings.pop(); - const loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); + delete bindingByName[ trackName ]; - return new Promise( ( resolve, reject ) => { + if ( Object.keys( bindingByName ).length === 0 ) { - loader.load( url, resolve, undefined, reject ); + delete bindingsByRoot[ rootUuid ]; - } ); + } } - preload() { + _lendBinding( binding ) { - this._initDecoder(); + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - return this; + lastActiveIndex = this._nActiveBindings ++, - } + firstInactiveBinding = bindings[ lastActiveIndex ]; - _initDecoder() { + binding._cacheIndex = lastActiveIndex; + bindings[ lastActiveIndex ] = binding; - if ( this.decoderPending ) return this.decoderPending; + firstInactiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = firstInactiveBinding; - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; + } - if ( useJS ) { + _takeBackBinding( binding ) { - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - } else { + firstInactiveIndex = -- this._nActiveBindings, - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); + lastActiveBinding = bindings[ firstInactiveIndex ]; - } + binding._cacheIndex = firstInactiveIndex; + bindings[ firstInactiveIndex ] = binding; - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { + lastActiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = lastActiveBinding; - const jsContent = libraries[ 0 ]; + } - if ( ! useJS ) { - this.decoderConfig.wasmBinary = libraries[ 1 ]; + // Memory management of Interpolants for weight and time scale - } + _lendControlInterpolant() { - const fn = DRACOWorker.toString(); + const interpolants = this._controlInterpolants, + lastActiveIndex = this._nActiveControlInterpolants ++; - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); + let interpolant = interpolants[ lastActiveIndex ]; - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); + if ( interpolant === undefined ) { - } ); + interpolant = new LinearInterpolant( + new Float32Array( 2 ), new Float32Array( 2 ), + 1, this._controlInterpolantsResultBuffer ); - return this.decoderPending; + interpolant.__cacheIndex = lastActiveIndex; + interpolants[ lastActiveIndex ] = interpolant; - } + } - _getWorker( taskID, taskCost ) { + return interpolant; - return this._initDecoder().then( () => { + } - if ( this.workerPool.length < this.workerLimit ) { + _takeBackControlInterpolant( interpolant ) { - const worker = new Worker( this.workerSourceURL ); + const interpolants = this._controlInterpolants, + prevIndex = interpolant.__cacheIndex, - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; + firstInactiveIndex = -- this._nActiveControlInterpolants, - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); + lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - worker.onmessage = function ( e ) { + interpolant.__cacheIndex = firstInactiveIndex; + interpolants[ firstInactiveIndex ] = interpolant; - const message = e.data; + lastActiveInterpolant.__cacheIndex = prevIndex; + interpolants[ prevIndex ] = lastActiveInterpolant; - switch ( message.type ) { + } - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; + // return an action for a clip optionally using a custom root target + // object (this method allocates a lot of dynamic memory in case a + // previously unknown clip/root combination is specified) + clipAction( clip, optionalRoot, blendMode ) { - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; + const root = optionalRoot || this._root, + rootUuid = root.uuid; - default: - console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); + let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - } + const clipUuid = clipObject !== null ? clipObject.uuid : clip; - }; + const actionsForClip = this._actionsByClip[ clipUuid ]; + let prototypeAction = null; - this.workerPool.push( worker ); + if ( blendMode === undefined ) { - } else { + if ( clipObject !== null ) { - this.workerPool.sort( function ( a, b ) { + blendMode = clipObject.blendMode; - return a._taskLoad > b._taskLoad ? - 1 : 1; + } else { - } ); + blendMode = NormalAnimationBlendMode; } - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; - - } ); + } - } + if ( actionsForClip !== undefined ) { - _releaseTask( worker, taskID ) { + const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; + if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - } + return existingAction; - debug() { + } - console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); + // we know the clip, so we don't have to parse all + // the bindings again but can just copy + prototypeAction = actionsForClip.knownActions[ 0 ]; - } + // also, take the clip from the prototype action + if ( clipObject === null ) + clipObject = prototypeAction._clip; - dispose() { + } - for ( let i = 0; i < this.workerPool.length; ++ i ) { + // clip must be known when specified via string + if ( clipObject === null ) return null; - this.workerPool[ i ].terminate(); + // allocate all resources required to run it + const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - } + this._bindAction( newAction, prototypeAction ); - this.workerPool.length = 0; + // and make the action known to the memory manager + this._addInactiveAction( newAction, clipUuid, rootUuid ); - return this; + return newAction; } - } - - /* WEB WORKER */ + // get an existing action + existingAction( clip, optionalRoot ) { - function DRACOWorker() { + const root = optionalRoot || this._root, + rootUuid = root.uuid, - let decoderConfig; - let decoderPending; + clipObject = typeof clip === 'string' ? + AnimationClip.findByName( root, clip ) : clip, - onmessage = function ( e ) { + clipUuid = clipObject ? clipObject.uuid : clip, - const message = e.data; + actionsForClip = this._actionsByClip[ clipUuid ]; - switch ( message.type ) { + if ( actionsForClip !== undefined ) { - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { + return actionsForClip.actionByRoot[ rootUuid ] || null; - decoderConfig.onModuleLoaded = function ( draco ) { + } - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); + return null; - }; + } - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef + // deactivates all previously scheduled actions + stopAllAction() { - } ); - break; + const actions = this._actions, + nActions = this._nActiveActions; - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { + for ( let i = nActions - 1; i >= 0; -- i ) { - const draco = module.draco; - const decoder = new draco.Decoder(); - const decoderBuffer = new draco.DecoderBuffer(); - decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); + actions[ i ].stop(); - try { + } - const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); + return this; - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); + } - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); + // advance the time and update apply the animation + update( deltaTime ) { - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); + deltaTime *= this.timeScale; - } catch ( error ) { + const actions = this._actions, + nActions = this._nActiveActions, - console.error( error ); + time = this.time += deltaTime, + timeDirection = Math.sign( deltaTime ), - self.postMessage( { type: 'error', id: message.id, error: error.message } ); + accuIndex = this._accuIndex ^= 1; - } finally { + // run active actions - draco.destroy( decoderBuffer ); - draco.destroy( decoder ); + for ( let i = 0; i !== nActions; ++ i ) { - } + const action = actions[ i ]; - } ); - break; + action._update( time, deltaTime, timeDirection, accuIndex ); } - }; - - function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { + // update scene graph - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; + const bindings = this._bindings, + nBindings = this._nActiveBindings; - let dracoGeometry; - let decodingStatus; + for ( let i = 0; i !== nBindings; ++ i ) { - const geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); + bindings[ i ].apply( accuIndex ); - if ( geometryType === draco.TRIANGULAR_MESH ) { + } - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); + return this; - } else if ( geometryType === draco.POINT_CLOUD ) { + } - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); + // Allows you to seek to a specific time in an animation. + setTime( timeInSeconds ) { - } else { + this.time = 0; // Zero out time attribute for AnimationMixer object; + for ( let i = 0; i < this._actions.length; i ++ ) { - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); + this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. } - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { + return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); + } - } + // return this mixer's root target object + getRoot() { - const geometry = { index: null, attributes: [] }; + return this._root; - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { + } - const attributeType = self[ attributeTypes[ attributeName ] ]; + // free all resources specific to a particular clip + uncacheClip( clip ) { - let attribute; - let attributeID; + const actions = this._actions, + clipUuid = clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ]; - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { + if ( actionsForClip !== undefined ) { - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); + // note: just calling _removeInactiveAction would mess up the + // iteration state and also require updating the state we can + // just throw away - } else { + const actionsToRemove = actionsForClip.knownActions; - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); + for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - if ( attributeID === - 1 ) continue; + const action = actionsToRemove[ i ]; - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); + this._deactivateAction( action ); - } + const cacheIndex = action._cacheIndex, + lastInactiveAction = actions[ actions.length - 1 ]; - geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); + action._cacheIndex = null; + action._byClipCacheIndex = null; - } + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { + this._removeInactiveBindingsForAction( action ); - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); + } + + delete actionsByClip[ clipUuid ]; } - draco.destroy( dracoGeometry ); + } - return geometry; + // free all resources specific to a particular root target object + uncacheRoot( root ) { - } + const rootUuid = root.uuid, + actionsByClip = this._actionsByClip; - function decodeIndex( draco, decoder, dracoGeometry ) { + for ( const clipUuid in actionsByClip ) { - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; + const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, + action = actionByRoot[ rootUuid ]; - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); + if ( action !== undefined ) { - return { array: index, itemSize: 1 }; + this._deactivateAction( action ); + this._removeInactiveAction( action ); - } + } - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { + } - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); + const bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ]; - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); + if ( bindingByName !== undefined ) { - return { - name: attributeName, - array: array, - itemSize: numComponents - }; + for ( const trackName in bindingByName ) { + + const binding = bindingByName[ trackName ]; + binding.restoreOriginalState(); + this._removeInactiveBinding( binding ); + + } + + } } - function getDracoDataType( draco, attributeType ) { + // remove a targeted clip from the cache + uncacheAction( clip, optionalRoot ) { - switch ( attributeType ) { + const action = this.existingAction( clip, optionalRoot ); - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; + if ( action !== null ) { + + this._deactivateAction( action ); + this._removeInactiveAction( action ); } @@ -42848,175 +42758,227 @@ } - class GLTFLoader extends Loader { + AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); - constructor( manager ) { + class InstancedInterleavedBuffer extends InterleavedBuffer { - super( manager ); + constructor( array, stride, meshPerAttribute = 1 ) { - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; + super( array, stride ); - this.pluginCallbacks = []; + this.meshPerAttribute = meshPerAttribute; - this.register( function ( parser ) { + } - return new GLTFMaterialsClearcoatExtension( parser ); + copy( source ) { - } ); + super.copy( source ); - this.register( function ( parser ) { + this.meshPerAttribute = source.meshPerAttribute; - return new GLTFTextureBasisUExtension( parser ); + return this; - } ); + } - this.register( function ( parser ) { + clone( data ) { - return new GLTFTextureWebPExtension( parser ); + const ib = super.clone( data ); - } ); + ib.meshPerAttribute = this.meshPerAttribute; - this.register( function ( parser ) { + return ib; - return new GLTFMaterialsTransmissionExtension( parser ); + } - } ); + toJSON( data ) { - this.register( function ( parser ) { + const json = super.toJSON( data ); - return new GLTFLightsExtension( parser ); + json.isInstancedInterleavedBuffer = true; + json.meshPerAttribute = this.meshPerAttribute; - } ); + return json; - this.register( function ( parser ) { + } - return new GLTFMeshoptCompression( parser ); + } - } ); + InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; + + class Raycaster { + + constructor( origin, direction, near = 0, far = Infinity ) { + + this.ray = new Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near; + this.far = far; + this.camera = null; + this.layers = new Layers(); + + this.params = { + Mesh: {}, + Line: { threshold: 1 }, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; } - load( url, onLoad, onProgress, onError ) { + set( origin, direction ) { - const scope = this; + // direction is assumed to be normalized (for accurate distance calculations) - let resourcePath; + this.ray.set( origin, direction ); - if ( this.resourcePath !== '' ) { + } - resourcePath = this.resourcePath; + setFromCamera( coords, camera ) { - } else if ( this.path !== '' ) { + if ( camera.isPerspectiveCamera ) { - resourcePath = this.path; + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + this.camera = camera; + + } else if ( camera.isOrthographicCamera ) { + + this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + this.camera = camera; } else { - resourcePath = LoaderUtils.extractUrlBase( url ); + console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); } - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); - - const _onError = function ( e ) { + } - if ( onError ) { + intersectObject( object, recursive = true, intersects = [] ) { - onError( e ); + intersectObject( object, this, intersects, recursive ); - } else { + intersects.sort( ascSort ); - console.error( e ); + return intersects; - } + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + intersectObjects( objects, recursive = true, intersects = [] ) { - }; + for ( let i = 0, l = objects.length; i < l; i ++ ) { - const loader = new FileLoader( this.manager ); + intersectObject( objects[ i ], this, intersects, recursive ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); + } - loader.load( url, function ( data ) { + intersects.sort( ascSort ); - try { + return intersects; - scope.parse( data, resourcePath, function ( gltf ) { + } - onLoad( gltf ); + } - scope.manager.itemEnd( url ); + function ascSort( a, b ) { - }, _onError ); + return a.distance - b.distance; - } catch ( e ) { + } - _onError( e ); + function intersectObject( object, raycaster, intersects, recursive ) { - } + if ( object.layers.test( raycaster.layers ) ) { - }, onProgress, _onError ); + object.raycast( raycaster, intersects ); } - setDRACOLoader( dracoLoader ) { + if ( recursive === true ) { - this.dracoLoader = dracoLoader; - return this; + const children = object.children; + + for ( let i = 0, l = children.length; i < l; i ++ ) { + + intersectObject( children[ i ], raycaster, intersects, true ); + + } } - setDDSLoader() { + } - throw new Error( + /** + * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system + * + * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. + * The azimuthal angle (theta) is measured from the positive z-axis. + */ - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' + class Spherical { - ); + constructor( radius = 1, phi = 0, theta = 0 ) { + + this.radius = radius; + this.phi = phi; // polar angle + this.theta = theta; // azimuthal angle + + return this; } - setKTX2Loader( ktx2Loader ) { + set( radius, phi, theta ) { + + this.radius = radius; + this.phi = phi; + this.theta = theta; - this.ktx2Loader = ktx2Loader; return this; } - setMeshoptDecoder( meshoptDecoder ) { + copy( other ) { + + this.radius = other.radius; + this.phi = other.phi; + this.theta = other.theta; - this.meshoptDecoder = meshoptDecoder; return this; } - register( callback ) { + // restrict phi to be between EPS and PI-EPS + makeSafe() { - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { + const EPS = 0.000001; + this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); - this.pluginCallbacks.push( callback ); + return this; - } + } - return this; + setFromVector3( v ) { + + return this.setFromCartesianCoords( v.x, v.y, v.z ); } - unregister( callback ) { + setFromCartesianCoords( x, y, z ) { - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { + this.radius = Math.sqrt( x * x + y * y + z * z ); - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); + if ( this.radius === 0 ) { + + this.theta = 0; + this.phi = 0; + + } else { + + this.theta = Math.atan2( x, z ); + this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); } @@ -43024,4319 +42986,7368 @@ } - parse( data, path, onLoad, onError ) { + clone() { - let content; - const extensions = {}; - const plugins = {}; + return new this.constructor().copy( this ); - if ( typeof data === 'string' ) { + } - content = data; + } - } else { + const _vector$2 = /*@__PURE__*/ new Vector3(); + const _boneMatrix = /*@__PURE__*/ new Matrix4(); + const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); - const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + class SkeletonHelper extends LineSegments { - try { + constructor( object ) { - extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); + const bones = getBoneList( object ); - } catch ( error ) { + const geometry = new BufferGeometry(); - if ( onError ) onError( error ); - return; + const vertices = []; + const colors = []; - } + const color1 = new Color( 0, 0, 1 ); + const color2 = new Color( 0, 1, 0 ); - content = extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content; + for ( let i = 0; i < bones.length; i ++ ) { - } else { + const bone = bones[ i ]; - content = LoaderUtils.decodeText( new Uint8Array( data ) ); + if ( bone.parent && bone.parent.isBone ) { + + vertices.push( 0, 0, 0 ); + vertices.push( 0, 0, 0 ); + colors.push( color1.r, color1.g, color1.b ); + colors.push( color2.r, color2.g, color2.b ); } } - const json = JSON.parse( content ); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { + const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; + super( geometry, material ); - } + this.type = 'SkeletonHelper'; + this.isSkeletonHelper = true; - const parser = new GLTFParser$1( json, { + this.root = object; + this.bones = bones; - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - } ); + } - parser.fileLoader.setRequestHeader( this.requestHeader ); + updateMatrixWorld( force ) { - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { + const bones = this.bones; - const plugin = this.pluginCallbacks[ i ]( parser ); - plugins[ plugin.name ] = plugin; + const geometry = this.geometry; + const position = geometry.getAttribute( 'position' ); - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; + _matrixWorldInv.copy( this.root.matrixWorld ).invert(); - } + for ( let i = 0, j = 0; i < bones.length; i ++ ) { - if ( json.extensionsUsed ) { + const bone = bones[ i ]; - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { + if ( bone.parent && bone.parent.isBone ) { - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); - switch ( extensionName ) { + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); - case EXTENSIONS$1.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; + j += 2; - case EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); - break; + } - case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; + } - case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; + geometry.getAttribute( 'position' ).needsUpdate = true; - case EXTENSIONS$1.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; + super.updateMatrixWorld( force ); - default: + } - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { + } - console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - } + function getBoneList( object ) { - } + const boneList = []; - } + if ( object.isBone === true ) { - } + boneList.push( object ); - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); + } + + for ( let i = 0; i < object.children.length; i ++ ) { + + boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } + return boneList; + } - /* GLTFREGISTRY */ + class GridHelper extends LineSegments { - function GLTFRegistry$1() { + constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { - let objects = {}; + color1 = new Color( color1 ); + color2 = new Color( color2 ); - return { + const center = divisions / 2; + const step = size / divisions; + const halfSize = size / 2; - get: function ( key ) { + const vertices = [], colors = []; - return objects[ key ]; + for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - }, + vertices.push( - halfSize, 0, k, halfSize, 0, k ); + vertices.push( k, 0, - halfSize, k, 0, halfSize ); - add: function ( key, object ) { + const color = i === center ? color1 : color2; - objects[ key ] = object; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; - }, + } - remove: function ( key ) { + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - delete objects[ key ]; + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - }, + super( geometry, material ); - removeAll: function () { + this.type = 'GridHelper'; - objects = {}; + } - } + } - }; + const _baseTable = new Uint32Array( 512 ); + const _shiftTable = new Uint32Array( 512 ); - } + for ( let i = 0; i < 256; ++ i ) { - /*********************************/ - /********** EXTENSIONS ***********/ - /*********************************/ + const e = i - 127; - const EXTENSIONS$1 = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' - }; + // very small number (0, -0) - /** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ - class GLTFLightsExtension { + if ( e < - 27 ) { - constructor( parser ) { + _baseTable[ i ] = 0x0000; + _baseTable[ i | 0x100 ] = 0x8000; + _shiftTable[ i ] = 24; + _shiftTable[ i | 0x100 ] = 24; - this.parser = parser; - this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; + // small number (denorm) - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; + } else if ( e < - 14 ) { - } + _baseTable[ i ] = 0x0400 >> ( - e - 14 ); + _baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; + _shiftTable[ i ] = - e - 1; + _shiftTable[ i | 0x100 ] = - e - 1; - _markDefs() { + // normal number - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; + } else if ( e <= 15 ) { - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + _baseTable[ i ] = ( e + 15 ) << 10; + _baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; + _shiftTable[ i ] = 13; + _shiftTable[ i | 0x100 ] = 13; - const nodeDef = nodeDefs[ nodeIndex ]; + // large number (Infinity, -Infinity) - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { + } else if ( e < 128 ) { - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); + _baseTable[ i ] = 0x7c00; + _baseTable[ i | 0x100 ] = 0xfc00; + _shiftTable[ i ] = 24; + _shiftTable[ i | 0x100 ] = 24; - } + // stay (NaN, Infinity, -Infinity) - } + } else { + + _baseTable[ i ] = 0x7c00; + _baseTable[ i | 0x100 ] = 0xfc00; + _shiftTable[ i ] = 13; + _shiftTable[ i | 0x100 ] = 13; } - _loadLight( lightIndex ) { + } - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); + // float16 to float32 helpers - if ( dependency ) return dependency; - - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; - - const color = new Color( 0xffffff ); - - if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); + const _mantissaTable = new Uint32Array( 2048 ); + const _exponentTable = new Uint32Array( 64 ); + const _offsetTable = new Uint32Array( 64 ); - const range = lightDef.range !== undefined ? lightDef.range : 0; + for ( let i = 1; i < 1024; ++ i ) { - switch ( lightDef.type ) { + let m = i << 13; // zero pad mantissa bits + let e = 0; // zero exponent - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; + // normalized + while ( ( m & 0x00800000 ) === 0 ) { - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; + m <<= 1; + e -= 0x00800000; // decrement exponent - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; + } - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); + m &= ~ 0x00800000; // clear leading 1 bit + e += 0x38800000; // adjust bias - } + _mantissaTable[ i ] = m | e; - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); + } - lightNode.decay = 2; + for ( let i = 1024; i < 2048; ++ i ) { - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; + _mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); + } - dependency = Promise.resolve( lightNode ); + for ( let i = 1; i < 31; ++ i ) { - parser.cache.add( cacheKey, dependency ); + _exponentTable[ i ] = i << 23; - return dependency; + } - } + _exponentTable[ 31 ] = 0x47800000; + _exponentTable[ 32 ] = 0x80000000; + for ( let i = 33; i < 63; ++ i ) { - createNodeAttachment( nodeIndex ) { + _exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; + } - if ( lightIndex === undefined ) return null; + _exponentTable[ 63 ] = 0xc7800000; - return this._loadLight( lightIndex ).then( function ( light ) { + for ( let i = 1; i < 64; ++ i ) { - return parser._getNodeRef( self.cache, lightIndex, light ); + if ( i !== 32 ) { - } ); + _offsetTable[ i ] = 1024; } } - /** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ - class GLTFMaterialsUnlitExtension { + // - constructor() { + Curve.create = function ( construct, getPoint ) { - this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; + console.log( 'THREE.Curve.create() has been deprecated' ); - } + construct.prototype = Object.create( Curve.prototype ); + construct.prototype.constructor = construct; + construct.prototype.getPoint = getPoint; - getMaterialType() { + return construct; - return MeshBasicMaterial; + }; - } + // - extendParams( materialParams, materialDef, parser ) { + Path.prototype.fromPoints = function ( points ) { - const pending = []; + console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); + return this.setFromPoints( points ); - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; + }; - const metallicRoughness = materialDef.pbrMetallicRoughness; + GridHelper.prototype.setColors = function () { - if ( metallicRoughness ) { + console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + }; - const array = metallicRoughness.baseColorFactor; + SkeletonHelper.prototype.update = function () { - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; + console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); - } + }; - if ( metallicRoughness.baseColorTexture !== undefined ) { + // - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); + Loader.prototype.extractUrlBase = function ( url ) { - } + console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); + return LoaderUtils.extractUrlBase( url ); - } + }; - return Promise.all( pending ); + Loader.Handlers = { - } + add: function ( /* regex, loader */ ) { - } + console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - /** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ - class GLTFMaterialsClearcoatExtension { + }, - constructor( parser ) { + get: function ( /* file */ ) { - this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; + console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); } - getMaterialType( materialIndex ) { + }; - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + // - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; + Box3.prototype.center = function ( optionalTarget ) { - return MeshPhysicalMaterial; + console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); + return this.getCenter( optionalTarget ); - } + }; - extendMaterialParams( materialIndex, materialParams ) { + Box3.prototype.empty = function () { - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { + }; - return Promise.resolve(); + Box3.prototype.isIntersectionBox = function ( box ) { - } + console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); - const pending = []; + }; - const extension = materialDef.extensions[ this.name ]; + Box3.prototype.isIntersectionSphere = function ( sphere ) { - if ( extension.clearcoatFactor !== undefined ) { + console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); - materialParams.clearcoat = extension.clearcoatFactor; + }; - } + Box3.prototype.size = function ( optionalTarget ) { - if ( extension.clearcoatTexture !== undefined ) { + console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); + return this.getSize( optionalTarget ); - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); + }; - } + // - if ( extension.clearcoatRoughnessFactor !== undefined ) { + Euler.prototype.toVector3 = function () { - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; + console.error( 'THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead' ); - } + }; - if ( extension.clearcoatRoughnessTexture !== undefined ) { - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); + // - } + Sphere.prototype.empty = function () { - if ( extension.clearcoatNormalTexture !== undefined ) { + console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); + }; - if ( extension.clearcoatNormalTexture.scale !== undefined ) { + // - const scale = extension.clearcoatNormalTexture.scale; + Frustum.prototype.setFromMatrix = function ( m ) { - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.clearcoatNormalScale = new Vector2( scale, - scale ); + console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); + return this.setFromProjectionMatrix( m ); - } + }; - } + // - return Promise.all( pending ); + Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { - } + console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - } + }; - /** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ - class GLTFMaterialsTransmissionExtension { + Matrix3.prototype.multiplyVector3 = function ( vector ) { - constructor( parser ) { + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); - this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; + }; - } + Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { - getMaterialType( materialIndex ) { + console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + }; - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; + Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { - return MeshPhysicalMaterial; + console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); + return attribute.applyMatrix3( this ); - } + }; - extendMaterialParams( materialIndex, materialParams ) { + Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { + }; - return Promise.resolve(); + Matrix3.prototype.getInverse = function ( matrix ) { - } + console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - const pending = []; + }; - const extension = materialDef.extensions[ this.name ]; + // - if ( extension.transmissionFactor !== undefined ) { + Matrix4.prototype.extractPosition = function ( m ) { - materialParams.transmission = extension.transmissionFactor; + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); - } + }; - if ( extension.transmissionTexture !== undefined ) { + Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); + console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - } + }; - return Promise.all( pending ); + Matrix4.prototype.getPosition = function () { - } + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + return new Vector3().setFromMatrixColumn( this, 3 ); - } + }; - /** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ - class GLTFTextureBasisUExtension { + Matrix4.prototype.setRotationFromQuaternion = function ( q ) { - constructor( parser ) { + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + return this.makeRotationFromQuaternion( q ); - this.parser = parser; - this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; + }; - } + Matrix4.prototype.multiplyToArray = function () { - loadTexture( textureIndex ) { + console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - const parser = this.parser; - const json = parser.json; + }; - const textureDef = json.textures[ textureIndex ]; + Matrix4.prototype.multiplyVector3 = function ( vector ) { - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - return null; + }; - } + Matrix4.prototype.multiplyVector4 = function ( vector ) { - const extension = textureDef.extensions[ this.name ]; - const source = json.images[ extension.source ]; - const loader = parser.options.ktx2Loader; + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - if ( ! loader ) { + }; - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { + Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); + console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - } else { + }; - // Assumes that the extension is optional and that a fallback texture is present - return null; + Matrix4.prototype.rotateAxis = function ( v ) { - } + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + v.transformDirection( this ); - } + }; - return parser.loadTextureImage( textureIndex, source, loader ); + Matrix4.prototype.crossVector = function ( vector ) { - } + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - } + }; - /** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ - class GLTFTextureWebPExtension { + Matrix4.prototype.translate = function () { - constructor( parser ) { + console.error( 'THREE.Matrix4: .translate() has been removed.' ); - this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; - this.isSupported = null; + }; - } + Matrix4.prototype.rotateX = function () { - loadTexture( textureIndex ) { + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - const name = this.name; - const parser = this.parser; - const json = parser.json; + }; - const textureDef = json.textures[ textureIndex ]; + Matrix4.prototype.rotateY = function () { - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - return null; + }; - } + Matrix4.prototype.rotateZ = function () { - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - let loader = parser.textureLoader; - if ( source.uri ) { + }; - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; + Matrix4.prototype.rotateByAxis = function () { - } + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - return this.detectSupport().then( function ( isSupported ) { + }; - if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader ); + Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { + console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); + return attribute.applyMatrix4( this ); - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); + }; - } + Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); + console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - } ); + }; - } + Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { - detectSupport() { + console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); + return this.makePerspective( left, right, top, bottom, near, far ); - if ( ! this.isSupported ) { + }; - this.isSupported = new Promise( function ( resolve ) { + Matrix4.prototype.getInverse = function ( matrix ) { - const image = new Image(); + console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; + }; - image.onload = image.onerror = function () { + // - resolve( image.height === 1 ); + Plane.prototype.isIntersectionLine = function ( line ) { - }; + console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); + return this.intersectsLine( line ); - } ); + }; - } + // - return this.isSupported; + Quaternion.prototype.multiplyVector3 = function ( vector ) { - } + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); - } + }; - /** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ - class GLTFMeshoptCompression { + Quaternion.prototype.inverse = function ( ) { - constructor( parser ) { + console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); + return this.invert(); - this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; - this.parser = parser; + }; - } + // - loadBufferView( index ) { + Ray.prototype.isIntersectionBox = function ( box ) { - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; + console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { + }; - const extensionDef = bufferView.extensions[ this.name ]; + Ray.prototype.isIntersectionPlane = function ( plane ) { - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; + console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); + return this.intersectsPlane( plane ); - if ( ! decoder || ! decoder.supported ) { + }; - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { + Ray.prototype.isIntersectionSphere = function ( sphere ) { - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); + console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); - } else { + }; - // Assumes that the extension is optional and that fallback buffer data is present - return null; + // - } + Triangle.prototype.area = function () { - } + console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); + return this.getArea(); - return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { + }; - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; + Triangle.prototype.barycoordFromPoint = function ( point, target ) { - const count = extensionDef.count; - const stride = extensionDef.byteStride; + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return this.getBarycoord( point, target ); - const result = new ArrayBuffer( count * stride ); - const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); + }; - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; + Triangle.prototype.midpoint = function ( target ) { - } ); + console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); + return this.getMidpoint( target ); - } else { + }; - return null; + Triangle.prototypenormal = function ( target ) { - } + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return this.getNormal( target ); - } + }; - } + Triangle.prototype.plane = function ( target ) { - /* BINARY EXTENSION */ - const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; - const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; - const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); + return this.getPlane( target ); - class GLTFBinaryExtension$1 { + }; - constructor( data ) { + Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { - this.name = EXTENSIONS$1.KHR_BINARY_GLTF; - this.content = null; - this.body = null; + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return Triangle.getBarycoord( point, a, b, c, target ); - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); + }; - this.header = { - magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; + Triangle.normal = function ( a, b, c, target ) { - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return Triangle.getNormal( a, b, c, target ); - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); + }; - } else if ( this.header.version < 2.0 ) { + // - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); + Shape.prototype.extractAllPoints = function ( divisions ) { - } + console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); + return this.extractPoints( divisions ); - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); - let chunkIndex = 0; - - while ( chunkIndex < chunkContentsLength ) { + }; - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; + Shape.prototype.extrude = function ( options ) { - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; + console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); + return new ExtrudeGeometry( this, options ); - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { + }; - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); - this.content = LoaderUtils.decodeText( contentArray ); + Shape.prototype.makeGeometry = function ( options ) { - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); + return new ShapeGeometry( this, options ); - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); + }; - } + // - // Clients must ignore chunks with unknown types. + Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { - chunkIndex += chunkLength; + console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - } + }; - if ( this.content === null ) { + Vector2.prototype.distanceToManhattan = function ( v ) { - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); + console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - } + }; - } + Vector2.prototype.lengthManhattan = function () { - } + console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - /** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ - class GLTFDracoMeshCompressionExtension { + }; - constructor( json, dracoLoader ) { + // - if ( ! dracoLoader ) { + Vector3.prototype.setEulerFromRotationMatrix = function () { - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - } + }; - this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); + Vector3.prototype.setEulerFromQuaternion = function () { - } + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - decodePrimitive( primitive, parser ) { + }; - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; + Vector3.prototype.getPositionFromMatrix = function ( m ) { - for ( const attributeName in gltfAttributeMap ) { + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + return this.setFromMatrixPosition( m ); - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); + }; - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; + Vector3.prototype.getScaleFromMatrix = function ( m ) { - } + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + return this.setFromMatrixScale( m ); - for ( const attributeName in primitive.attributes ) { + }; - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); + Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { - if ( gltfAttributeMap[ attributeName ] !== undefined ) { + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + return this.setFromMatrixColumn( matrix, index ); - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + }; - attributeTypeMap[ threeAttributeName ] = componentType; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; + Vector3.prototype.applyProjection = function ( m ) { - } + console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); + return this.applyMatrix4( m ); - } + }; - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { + Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { - return new Promise( function ( resolve ) { + console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { + }; - for ( const attributeName in geometry.attributes ) { + Vector3.prototype.distanceToManhattan = function ( v ) { - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; + console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - if ( normalized !== undefined ) attribute.normalized = normalized; + }; - } + Vector3.prototype.lengthManhattan = function () { - resolve( geometry ); + console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - }, threeAttributeMap, attributeTypeMap ); + }; - } ); + // - } ); + Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { - } + console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - } + }; - /** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ - class GLTFTextureTransformExtension { + Vector4.prototype.lengthManhattan = function () { - constructor() { + console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + }; - } + // - extendTexture( texture, transform ) { + Object3D.prototype.getChildByName = function ( name ) { - texture = texture.clone(); + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); - if ( transform.offset !== undefined ) { + }; - texture.offset.fromArray( transform.offset ); + Object3D.prototype.renderDepth = function () { - } + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - if ( transform.rotation !== undefined ) { + }; - texture.rotation = transform.rotation; + Object3D.prototype.translate = function ( distance, axis ) { - } + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); - if ( transform.scale !== undefined ) { + }; - texture.repeat.fromArray( transform.scale ); + Object3D.prototype.getWorldRotation = function () { - } + console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - if ( transform.texCoord !== undefined ) { + }; - console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); + Object3D.prototype.applyMatrix = function ( matrix ) { - } + console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); - texture.needsUpdate = true; + }; - return texture; + Object.defineProperties( Object3D.prototype, { - } + eulerOrder: { + get: function () { - } + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + return this.rotation.order; - /** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ + }, + set: function ( value ) { - /** - * A sub class of StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + this.rotation.order = value; - class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { + } + }, + useQuaternion: { + get: function () { - constructor( params ) { + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - super(); + }, + set: function () { - this.isGLTFSpecularGlossinessMaterial = true; + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - //various chunks that need replacing - const specularMapParsFragmentChunk = [ - '#ifdef USE_SPECULARMAP', - ' uniform sampler2D specularMap;', - '#endif' - ].join( '\n' ); + } + } - const glossinessMapParsFragmentChunk = [ - '#ifdef USE_GLOSSINESSMAP', - ' uniform sampler2D glossinessMap;', - '#endif' - ].join( '\n' ); + } ); - const specularMapFragmentChunk = [ - 'vec3 specularFactor = specular;', - '#ifdef USE_SPECULARMAP', - ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' specularFactor *= texelSpecular.rgb;', - '#endif' - ].join( '\n' ); + Mesh.prototype.setDrawMode = function () { - const glossinessMapFragmentChunk = [ - 'float glossinessFactor = glossiness;', - '#ifdef USE_GLOSSINESSMAP', - ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', - ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' glossinessFactor *= texelGlossiness.a;', - '#endif' - ].join( '\n' ); + console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - const lightPhysicalFragmentChunk = [ - 'PhysicalMaterial material;', - 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.specularRoughness += geometryRoughness;', - 'material.specularRoughness = min( material.specularRoughness, 1.0 );', - 'material.specularColor = specularFactor;', - ].join( '\n' ); + }; - const uniforms = { - specular: { value: new Color().setHex( 0xffffff ) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null } - }; + Object.defineProperties( Mesh.prototype, { - this._extraUniforms = uniforms; + drawMode: { + get: function () { - this.onBeforeCompile = function ( shader ) { + console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); + return TrianglesDrawMode; - for ( const uniformName in uniforms ) { + }, + set: function () { - shader.uniforms[ uniformName ] = uniforms[ uniformName ]; + console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - } + } + } - shader.fragmentShader = shader.fragmentShader - .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) - .replace( 'uniform float metalness;', 'uniform float glossiness;' ) - .replace( '#include ', specularMapParsFragmentChunk ) - .replace( '#include ', glossinessMapParsFragmentChunk ) - .replace( '#include ', specularMapFragmentChunk ) - .replace( '#include ', glossinessMapFragmentChunk ) - .replace( '#include ', lightPhysicalFragmentChunk ); + } ); - }; + SkinnedMesh.prototype.initBones = function () { - Object.defineProperties( this, { + console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - specular: { - get: function () { + }; - return uniforms.specular.value; + // - }, - set: function ( v ) { + PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - uniforms.specular.value = v; + console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' + + 'Use .setFocalLength and .filmGauge for a photographic setup.' ); - } - }, + if ( filmGauge !== undefined ) this.filmGauge = filmGauge; + this.setFocalLength( focalLength ); - specularMap: { - get: function () { + }; - return uniforms.specularMap.value; + // - }, - set: function ( v ) { + Object.defineProperties( Light.prototype, { + onlyShadow: { + set: function () { - uniforms.specularMap.value = v; + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - if ( v ) { + } + }, + shadowCameraFov: { + set: function ( value ) { - this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps + console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); + this.shadow.camera.fov = value; - } else { + } + }, + shadowCameraLeft: { + set: function ( value ) { - delete this.defines.USE_SPECULARMAP; + console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); + this.shadow.camera.left = value; - } + } + }, + shadowCameraRight: { + set: function ( value ) { - } - }, + console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); + this.shadow.camera.right = value; - glossiness: { - get: function () { + } + }, + shadowCameraTop: { + set: function ( value ) { - return uniforms.glossiness.value; + console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); + this.shadow.camera.top = value; - }, - set: function ( v ) { + } + }, + shadowCameraBottom: { + set: function ( value ) { - uniforms.glossiness.value = v; + console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); + this.shadow.camera.bottom = value; - } - }, + } + }, + shadowCameraNear: { + set: function ( value ) { - glossinessMap: { - get: function () { + console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); + this.shadow.camera.near = value; - return uniforms.glossinessMap.value; + } + }, + shadowCameraFar: { + set: function ( value ) { - }, - set: function ( v ) { + console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); + this.shadow.camera.far = value; - uniforms.glossinessMap.value = v; + } + }, + shadowCameraVisible: { + set: function () { - if ( v ) { + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); - this.defines.USE_GLOSSINESSMAP = ''; - this.defines.USE_UV = ''; + } + }, + shadowBias: { + set: function ( value ) { - } else { + console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); + this.shadow.bias = value; - delete this.defines.USE_GLOSSINESSMAP; - delete this.defines.USE_UV; + } + }, + shadowDarkness: { + set: function () { - } + console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); - } - } + } + }, + shadowMapWidth: { + set: function ( value ) { - } ); + console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); + this.shadow.mapSize.width = value; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; + } + }, + shadowMapHeight: { + set: function ( value ) { - this.setValues( params ); + console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); + this.shadow.mapSize.height = value; + } } + } ); - copy( source ) { - - super.copy( source ); - - this.specularMap = source.specularMap; - this.specular.copy( source.specular ); - this.glossinessMap = source.glossinessMap; - this.glossiness = source.glossiness; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - return this; + // - } + Object.defineProperties( BufferAttribute.prototype, { - } + length: { + get: function () { + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); + return this.array.length; - class GLTFMaterialsPbrSpecularGlossinessExtension { + } + }, + dynamic: { + get: function () { - constructor() { + console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); + return this.usage === DynamicDrawUsage; - this.name = EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; + }, + set: function ( /* value */ ) { - this.specularGlossinessParams = [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ]; + console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); + this.setUsage( DynamicDrawUsage ); + } } - getMaterialType() { + } ); - return GLTFMeshStandardSGMaterial; + BufferAttribute.prototype.setDynamic = function ( value ) { - } + console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - extendParams( materialParams, materialDef, parser ) { + }; - const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; + BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; + console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); - const pending = []; + }, - if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { + BufferAttribute.prototype.setArray = function ( /* array */ ) { - const array = pbrSpecularGlossiness.diffuseFactor; + console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; + }; - } + // - if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { + BufferGeometry.prototype.addIndex = function ( index ) { - pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); - } + }; - materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); - materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; - materialParams.specular = new Color( 1.0, 1.0, 1.0 ); + BufferGeometry.prototype.addAttribute = function ( name, attribute ) { - if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { + console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); - - } + if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; - pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); - pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); + return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - } + } - return Promise.all( pending ); + if ( name === 'index' ) { - } + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); - createMaterial( materialParams ) { + return this; - const material = new GLTFMeshStandardSGMaterial( materialParams ); - material.fog = true; + } - material.color = materialParams.color; + return this.setAttribute( name, attribute ); - material.map = materialParams.map === undefined ? null : materialParams.map; + }; - material.lightMap = null; - material.lightMapIntensity = 1.0; + BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; - material.aoMapIntensity = 1.0; + if ( indexOffset !== undefined ) { - material.emissive = materialParams.emissive; - material.emissiveIntensity = 1.0; - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; - material.bumpScale = 1; + } - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; - material.normalMapType = TangentSpaceNormalMap; + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); - if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; + }; - material.displacementMap = null; - material.displacementScale = 1; - material.displacementBias = 0; + BufferGeometry.prototype.clearDrawCalls = function () { - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; - material.specular = materialParams.specular; + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; - material.glossiness = materialParams.glossiness; + }; - material.alphaMap = null; + BufferGeometry.prototype.computeOffsets = function () { - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; - material.envMapIntensity = 1.0; + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - material.refractionRatio = 0.98; + }; - return material; + BufferGeometry.prototype.removeAttribute = function ( name ) { - } + console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - } + return this.deleteAttribute( name ); - /** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ - class GLTFMeshQuantizationExtension { + }; - constructor() { + BufferGeometry.prototype.applyMatrix = function ( matrix ) { - this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); - } + }; - } + Object.defineProperties( BufferGeometry.prototype, { - /*********************************/ - /********** INTERPOLATION ********/ - /*********************************/ + drawcalls: { + get: function () { - // Spline Interpolation - // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation - class GLTFCubicSplineInterpolant extends Interpolant { + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + } + }, + offsets: { + get: function () { - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; + } } - copySampleValue_( index ) { + } ); - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. + InterleavedBuffer.prototype.setDynamic = function ( value ) { - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; + console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - for ( let i = 0; i !== valueSize; i ++ ) { + }; - result[ i ] = values[ offset + i ]; + InterleavedBuffer.prototype.setArray = function ( /* array */ ) { - } + console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - return result; + }; - } + // - } + ExtrudeGeometry.prototype.getArrays = function () { - GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); - GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + }; - GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { + ExtrudeGeometry.prototype.addShapeList = function () { - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; + console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); - const stride2 = stride * 2; - const stride3 = stride * 3; + }; - const td = t1 - t0; + ExtrudeGeometry.prototype.addShape = function () { - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; + console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; + }; - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; + // - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { + Scene.prototype.dispose = function () { - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) + console.error( 'THREE.Scene: .dispose() has been removed.' ); - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; + }; - } + // - return result; + Object.defineProperties( Material.prototype, { - }; + wrapAround: { + get: function () { - /*********************************/ - /********** INTERNALS ************/ - /*********************************/ + console.warn( 'THREE.Material: .wrapAround has been removed.' ); - /* CONSTANTS */ + }, + set: function () { - const WEBGL_CONSTANTS$1 = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 - }; + console.warn( 'THREE.Material: .wrapAround has been removed.' ); - const WEBGL_COMPONENT_TYPES$1 = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array - }; + } + }, - const WEBGL_FILTERS$1 = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter - }; + overdraw: { + get: function () { - const WEBGL_WRAPPINGS$1 = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping - }; + console.warn( 'THREE.Material: .overdraw has been removed.' ); - const WEBGL_TYPE_SIZES$1 = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 - }; + }, + set: function () { - const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', - }; + console.warn( 'THREE.Material: .overdraw has been removed.' ); - const PATH_PROPERTIES$1 = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' - }; + } + }, - const INTERPOLATION$1 = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete - }; + wrapRGB: { + get: function () { - const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' - }; + console.warn( 'THREE.Material: .wrapRGB has been removed.' ); + return new Color(); - /* UTILITY FUNCTIONS */ + } + }, - function resolveURL$1( url, path ) { + shading: { + get: function () { - // Invalid URL - if ( typeof url !== 'string' || url === '' ) return ''; + console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - // Host Relative URL - if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { + }, + set: function ( value ) { - path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); + console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.flatShading = ( value === FlatShading ); - } + } + }, - // Absolute URL http://,https://,// - if ( /^(https?:)?\/\//i.test( url ) ) return url; + stencilMask: { + get: function () { - // Data URI - if ( /^data:.*,.*$/i.test( url ) ) return url; + console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); + return this.stencilFuncMask; - // Blob URL - if ( /^blob:.*$/i.test( url ) ) return url; + }, + set: function ( value ) { - // Relative URL - return path + url; + console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); + this.stencilFuncMask = value; - } + } + }, - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ - function createDefaultMaterial$1( cache ) { + vertexTangents: { + get: function () { - if ( cache[ 'DefaultMaterial' ] === undefined ) { + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); + }, + set: function () { - } + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - return cache[ 'DefaultMaterial' ]; + } + }, - } + } ); - function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { + Object.defineProperties( ShaderMaterial.prototype, { - // Add unknown glTF extensions to an object's userData. + derivatives: { + get: function () { - for ( const name in objectDef.extensions ) { + console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + return this.extensions.derivatives; - if ( knownExtensions[ name ] === undefined ) { + }, + set: function ( value ) { - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; + console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + this.extensions.derivatives = value; } - } - } + } ); - /** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ - function assignExtrasToUserData( object, gltfDef ) { + // - if ( gltfDef.extras !== undefined ) { + WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { - if ( typeof gltfDef.extras === 'object' ) { + console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); - Object.assign( object.userData, gltfDef.extras ); + }; - } else { + WebGLRenderer.prototype.animate = function ( callback ) { - console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); + console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); + this.setAnimationLoop( callback ); - } + }; - } + WebGLRenderer.prototype.getCurrentRenderTarget = function () { - } + console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); + return this.getRenderTarget(); - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ - function addMorphTargets( geometry, targets, parser ) { + }; - let hasMorphPosition = false; - let hasMorphNormal = false; + WebGLRenderer.prototype.getMaxAnisotropy = function () { - for ( let i = 0, il = targets.length; i < il; i ++ ) { + console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); + return this.capabilities.getMaxAnisotropy(); - const target = targets[ i ]; + }; - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; + WebGLRenderer.prototype.getPrecision = function () { - if ( hasMorphPosition && hasMorphNormal ) break; + console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); + return this.capabilities.precision; - } + }; - if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); + WebGLRenderer.prototype.resetGLState = function () { - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; + console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); + return this.state.reset(); - for ( let i = 0, il = targets.length; i < il; i ++ ) { + }; - const target = targets[ i ]; + WebGLRenderer.prototype.supportsFloatTextures = function () { - if ( hasMorphPosition ) { + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return this.extensions.get( 'OES_texture_float' ); - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; + }; - pendingPositionAccessors.push( pendingAccessor ); + WebGLRenderer.prototype.supportsHalfFloatTextures = function () { - } + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return this.extensions.get( 'OES_texture_half_float' ); - if ( hasMorphNormal ) { + }; - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; + WebGLRenderer.prototype.supportsStandardDerivatives = function () { - pendingNormalAccessors.push( pendingAccessor ); + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return this.extensions.get( 'OES_standard_derivatives' ); - } + }; - } + WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ) - ] ).then( function ( accessors ) { + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; + }; - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - geometry.morphTargetsRelative = true; + WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { - return geometry; + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - } ); + }; - } + WebGLRenderer.prototype.supportsBlendMinMax = function () { - /** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ - function updateMorphTargets( mesh, meshDef ) { + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return this.extensions.get( 'EXT_blend_minmax' ); - mesh.updateMorphTargets(); + }; - if ( meshDef.weights !== undefined ) { + WebGLRenderer.prototype.supportsVertexTextures = function () { - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { + console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); + return this.capabilities.vertexTextures; - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; + }; - } + WebGLRenderer.prototype.supportsInstancedArrays = function () { - } + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return this.extensions.get( 'ANGLE_instanced_arrays' ); - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { + }; - const targetNames = meshDef.extras.targetNames; + WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { - if ( mesh.morphTargetInfluences.length === targetNames.length ) { + console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); + this.setScissorTest( boolean ); - mesh.morphTargetDictionary = {}; + }; - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { + WebGLRenderer.prototype.initMaterial = function () { - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - } + }; - } else { + WebGLRenderer.prototype.addPrePlugin = function () { - console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - } + }; - } + WebGLRenderer.prototype.addPostPlugin = function () { - } + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - function createPrimitiveKey( primitiveDef ) { + }; - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; - let geometryKey; + WebGLRenderer.prototype.updateShadowMap = function () { - if ( dracoExtension ) { + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); + }; - } else { + WebGLRenderer.prototype.setFaceCulling = function () { - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; + console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - } + }; - return geometryKey; + WebGLRenderer.prototype.allocTextureUnit = function () { - } + console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - function createAttributesKey( attributes ) { + }; - let attributesKey = ''; + WebGLRenderer.prototype.setTexture = function () { - const keys = Object.keys( attributes ).sort(); + console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - for ( let i = 0, il = keys.length; i < il; i ++ ) { + }; - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; + WebGLRenderer.prototype.setTexture2D = function () { - } + console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - return attributesKey; + }; - } + WebGLRenderer.prototype.setTextureCube = function () { - function getNormalizedComponentScale( constructor ) { - - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data + console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - switch ( constructor ) { + }; - case Int8Array: - return 1 / 127; + WebGLRenderer.prototype.getActiveMipMapLevel = function () { - case Uint8Array: - return 1 / 255; + console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); + return this.getActiveMipmapLevel(); - case Int16Array: - return 1 / 32767; + }; - case Uint16Array: - return 1 / 65535; + Object.defineProperties( WebGLRenderer.prototype, { - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); + shadowMapEnabled: { + get: function () { - } + return this.shadowMap.enabled; - } + }, + set: function ( value ) { - /* GLTF PARSER */ + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + this.shadowMap.enabled = value; - class GLTFParser$1 { + } + }, + shadowMapType: { + get: function () { - constructor( json = {}, options = {} ) { + return this.shadowMap.type; - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; + }, + set: function ( value ) { - // loader object cache - this.cache = new GLTFRegistry$1(); + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + this.shadowMap.type = value; - // associations between Three.js objects and glTF elements - this.associations = new Map(); + } + }, + shadowMapCullFace: { + get: function () { - // BufferGeometry caching - this.primitiveCache = {}; + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); + return undefined; - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; + }, + set: function ( /* value */ ) { - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) { + } + }, + context: { + get: function () { - this.textureLoader = new ImageBitmapLoader( this.options.manager ); + console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); + return this.getContext(); - } else { + } + }, + vr: { + get: function () { - this.textureLoader = new TextureLoader( this.options.manager ); + console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); + return this.xr; } + }, + gammaInput: { + get: function () { - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); + console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); + return false; - if ( this.options.crossOrigin === 'use-credentials' ) { + }, + set: function () { - this.fileLoader.setWithCredentials( true ); + console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); } + }, + gammaOutput: { + get: function () { - } + console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); + return false; - setExtensions( extensions ) { + }, + set: function ( value ) { - this.extensions = extensions; + console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); + this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - } + } + }, + toneMappingWhitePoint: { + get: function () { - setPlugins( plugins ) { + console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); + return 1.0; - this.plugins = plugins; + }, + set: function () { - } + console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - parse( onLoad, onError ) { + } + }, + gammaFactor: { + get: function () { - const parser = this; - const json = this.json; - const extensions = this.extensions; + console.warn( 'THREE.WebGLRenderer: .gammaFactor has been removed.' ); + return 2; - // Clear the loader cache - this.cache.removeAll(); + }, + set: function () { - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { + console.warn( 'THREE.WebGLRenderer: .gammaFactor has been removed.' ); - return ext._markDefs && ext._markDefs(); + } + } + } ); - } ); + Object.defineProperties( WebGLShadowMap.prototype, { - Promise.all( this._invokeAll( function ( ext ) { + cullFace: { + get: function () { - return ext.beforeRoot && ext.beforeRoot(); + console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); + return undefined; - } ) ).then( function () { + }, + set: function ( /* cullFace */ ) { - return Promise.all( [ + console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), + } + }, + renderReverseSided: { + get: function () { - ] ); + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); + return undefined; - } ).then( function ( dependencies ) { + }, + set: function () { - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - addUnknownExtensionsToUserData( extensions, result, json ); + } + }, + renderSingleSided: { + get: function () { - assignExtrasToUserData( result, json ); + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); + return undefined; - Promise.all( parser._invokeAll( function ( ext ) { + }, + set: function () { - return ext.afterRoot && ext.afterRoot( result ); + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - } ) ).then( function () { + } + } - onLoad( result ); + } ); - } ); + // - } ).catch( onError ); + Object.defineProperties( WebGLRenderTarget.prototype, { - } + wrapS: { + get: function () { - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + return this.texture.wrapS; - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; + }, + set: function ( value ) { - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + this.texture.wrapS = value; - const joints = skinDefs[ skinIndex ].joints; + } + }, + wrapT: { + get: function () { - for ( let i = 0, il = joints.length; i < il; i ++ ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + return this.texture.wrapT; - nodeDefs[ joints[ i ] ].isBone = true; + }, + set: function ( value ) { - } + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + this.texture.wrapT = value; } + }, + magFilter: { + get: function () { - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + return this.texture.magFilter; - const nodeDef = nodeDefs[ nodeIndex ]; + }, + set: function ( value ) { - if ( nodeDef.mesh !== undefined ) { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + this.texture.magFilter = value; - this._addNodeRef( this.meshCache, nodeDef.mesh ); + } + }, + minFilter: { + get: function () { - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + return this.texture.minFilter; - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; + }, + set: function ( value ) { - } + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + this.texture.minFilter = value; - } + } + }, + anisotropy: { + get: function () { - if ( nodeDef.camera !== undefined ) { + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + return this.texture.anisotropy; - this._addNodeRef( this.cameraCache, nodeDef.camera ); + }, + set: function ( value ) { - } + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + this.texture.anisotropy = value; } + }, + offset: { + get: function () { - } + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + return this.texture.offset; - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { + }, + set: function ( value ) { - if ( index === undefined ) return; + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + this.texture.offset = value; - if ( cache.refs[ index ] === undefined ) { + } + }, + repeat: { + get: function () { - cache.refs[ index ] = cache.uses[ index ] = 0; + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + return this.texture.repeat; - } + }, + set: function ( value ) { - cache.refs[ index ] ++; + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + this.texture.repeat = value; - } + } + }, + format: { + get: function () { - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + return this.texture.format; - if ( cache.refs[ index ] <= 1 ) return object; + }, + set: function ( value ) { - const ref = object.clone(); + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + this.texture.format = value; - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); + } + }, + type: { + get: function () { - return ref; + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + return this.texture.type; - } + }, + set: function ( value ) { - _invokeOne( func ) { + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + this.texture.type = value; - const extensions = Object.values( this.plugins ); - extensions.push( this ); + } + }, + generateMipmaps: { + get: function () { - for ( let i = 0; i < extensions.length; i ++ ) { + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + return this.texture.generateMipmaps; - const result = func( extensions[ i ] ); + }, + set: function ( value ) { - if ( result ) return result; + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + this.texture.generateMipmaps = value; } + } - return null; + } ); - } + // - _invokeAll( func ) { + Audio.prototype.load = function ( file ) { - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); + console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); + const scope = this; + const audioLoader = new AudioLoader(); + audioLoader.load( file, function ( buffer ) { - const pending = []; + scope.setBuffer( buffer ); - for ( let i = 0; i < extensions.length; i ++ ) { + } ); + return this; - const result = func( extensions[ i ] ); + }; - if ( result ) pending.push( result ); + // - } + CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - return pending; + console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); + return this.update( renderer, scene ); - } + }; - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { + CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) { - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); + console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' ); + return this.renderTarget.clear( renderer, color, depth, stencil ); - if ( ! dependency ) { + }; - switch ( type ) { + ImageUtils.crossOrigin = undefined; - case 'scene': - dependency = this.loadScene( index ); - break; + ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - case 'node': - dependency = this.loadNode( index ); - break; + console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - case 'mesh': - dependency = this._invokeOne( function ( ext ) { + const loader = new TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - return ext.loadMesh && ext.loadMesh( index ); + const texture = loader.load( url, onLoad, undefined, onError ); - } ); - break; + if ( mapping ) texture.mapping = mapping; - case 'accessor': - dependency = this.loadAccessor( index ); - break; + return texture; - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { + }; - return ext.loadBufferView && ext.loadBufferView( index ); + ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - } ); - break; + console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - case 'buffer': - dependency = this.loadBuffer( index ); - break; + const loader = new CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - case 'material': - dependency = this._invokeOne( function ( ext ) { + const texture = loader.load( urls, onLoad, undefined, onError ); - return ext.loadMaterial && ext.loadMaterial( index ); + if ( mapping ) texture.mapping = mapping; - } ); - break; + return texture; - case 'texture': - dependency = this._invokeOne( function ( ext ) { + }; - return ext.loadTexture && ext.loadTexture( index ); + ImageUtils.loadCompressedTexture = function () { - } ); - break; + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - case 'skin': - dependency = this.loadSkin( index ); - break; + }; - case 'animation': - dependency = this.loadAnimation( index ); - break; + ImageUtils.loadCompressedTextureCube = function () { - case 'camera': - dependency = this.loadCamera( index ); - break; + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - default: - throw new Error( 'Unknown type: ' + type ); + }; - } + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - this.cache.add( cacheKey, dependency ); + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { + revision: REVISION, + } } ) ); - } + } - return dependency; + if ( typeof window !== 'undefined' ) { - } + if ( window.__THREE__ ) { - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { + console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); - let dependencies = this.cache.get( type ); + } else { - if ( ! dependencies ) { + window.__THREE__ = REVISION; - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; + } - dependencies = Promise.all( defs.map( function ( def, index ) { + } - return parser.getDependency( type, index ); + const _taskCache = new WeakMap(); - } ) ); + class DRACOLoader extends Loader { - this.cache.add( type, dependencies ); + constructor( manager ) { - } + super( manager ); - return dependencies; + this.decoderPath = ''; + this.decoderConfig = {}; + this.decoderBinary = null; + this.decoderPending = null; - } + this.workerLimit = 4; + this.workerPool = []; + this.workerNextTaskID = 1; + this.workerSourceURL = ''; - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { - - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; + this.defaultAttributeIDs = { + position: 'POSITION', + normal: 'NORMAL', + color: 'COLOR', + uv: 'TEX_COORD' + }; + this.defaultAttributeTypes = { + position: 'Float32Array', + normal: 'Float32Array', + color: 'Float32Array', + uv: 'Float32Array' + }; - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { + } - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); + setDecoderPath( path ) { - } + this.decoderPath = path; - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { + return this; - return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + } - } + setDecoderConfig( config ) { - const options = this.options; + this.decoderConfig = config; - return new Promise( function ( resolve, reject ) { + return this; - loader.load( resolveURL$1( bufferDef.uri, options.path ), resolve, undefined, function () { + } - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); + setWorkerLimit( workerLimit ) { - } ); + this.workerLimit = workerLimit; - } ); + return this; } - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { - - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; - - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { + load( url, onLoad, onProgress, onError ) { - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); + const loader = new FileLoader( this.manager ); - } ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); - } + loader.load( url, ( buffer ) => { - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { + const taskConfig = { + attributeIDs: this.defaultAttributeIDs, + attributeTypes: this.defaultAttributeTypes, + useUniqueIDs: false + }; - const parser = this; - const json = this.json; + this.decodeGeometry( buffer, taskConfig ) + .then( onLoad ) + .catch( onError ); - const accessorDef = this.json.accessors[ accessorIndex ]; + }, onProgress, onError ); - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { + } - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve( null ); + /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ + decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) { - } + const taskConfig = { + attributeIDs: attributeIDs || this.defaultAttributeIDs, + attributeTypes: attributeTypes || this.defaultAttributeTypes, + useUniqueIDs: !! attributeIDs + }; - const pendingBufferViews = []; + this.decodeGeometry( buffer, taskConfig ).then( callback ); - if ( accessorDef.bufferView !== undefined ) { + } - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); + decodeGeometry( buffer, taskConfig ) { - } else { + // TODO: For backward-compatibility, support 'attributeTypes' objects containing + // references (rather than names) to typed array constructors. These must be + // serialized before sending them to the worker. + for ( const attribute in taskConfig.attributeTypes ) { - pendingBufferViews.push( null ); + const type = taskConfig.attributeTypes[ attribute ]; - } + if ( type.BYTES_PER_ELEMENT !== undefined ) { - if ( accessorDef.sparse !== undefined ) { + taskConfig.attributeTypes[ attribute ] = type.name; - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); + } } - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { + // - const bufferView = bufferViews[ 0 ]; + const taskKey = JSON.stringify( taskConfig ); - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + // Check for an existing task using this buffer. A transferred buffer cannot be transferred + // again from this thread. + if ( _taskCache.has( buffer ) ) { - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; + const cachedTask = _taskCache.get( buffer ); - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { + if ( cachedTask.key === taskKey ) { - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); + return cachedTask.promise; - if ( ! ib ) { + } else if ( buffer.byteLength === 0 ) { - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); + // Technically, it would be possible to wait for the previous task to complete, + // transfer the buffer back, and decode again with the second configuration. That + // is complex, and I don't know of any reason to decode a Draco buffer twice in + // different ways, so this is left unimplemented. + throw new Error( - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); + 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + + 'settings. Buffer has already been transferred.' - parser.cache.add( ibCacheKey, ib ); + ); - } + } - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); + } - } else { + // - if ( bufferView === null ) { + let worker; + const taskID = this.workerNextTaskID ++; + const taskCost = buffer.byteLength; - array = new TypedArray( accessorDef.count * itemSize ); + // Obtain a worker and assign a task, and construct a geometry instance + // when the task completes. + const geometryPending = this._getWorker( taskID, taskCost ) + .then( ( _worker ) => { - } else { + worker = _worker; - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); + return new Promise( ( resolve, reject ) => { - } + worker._callbacks[ taskID ] = { resolve, reject }; - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); + worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - } + // this.debug(); - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { + } ); - const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + } ) + .then( ( message ) => this._createGeometry( message.geometry ) ); - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; + // Remove task from the task list. + // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) + geometryPending + .catch( () => true ) + .then( () => { - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); + if ( worker && taskID ) { - if ( bufferView !== null ) { + this._releaseTask( worker, taskID ); - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); + // this.debug(); } - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { + } ); - const index = sparseIndices[ i ]; + // Cache the task result. + _taskCache.set( buffer, { - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); + key: taskKey, + promise: geometryPending - } + } ); - } + return geometryPending; - return bufferAttribute; + } - } ); + _createGeometry( geometryData ) { - } + const geometry = new BufferGeometry(); - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { + if ( geometryData.index ) { - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const source = json.images[ textureDef.source ]; + geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); - let loader = this.textureLoader; + } - if ( source.uri ) { + for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - const handler = options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; + const attribute = geometryData.attributes[ i ]; + const name = attribute.name; + const array = attribute.array; + const itemSize = attribute.itemSize; + + geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); } - return this.loadTextureImage( textureIndex, source, loader ); + return geometry; } - loadTextureImage( textureIndex, source, loader ) { + _loadLibrary( url, responseType ) { - const parser = this; - const json = this.json; - const options = this.options; + const loader = new FileLoader( this.manager ); + loader.setPath( this.decoderPath ); + loader.setResponseType( responseType ); + loader.setWithCredentials( this.withCredentials ); - const textureDef = json.textures[ textureIndex ]; + return new Promise( ( resolve, reject ) => { - const URL = self.URL || self.webkitURL; + loader.load( url, resolve, undefined, reject ); - let sourceURI = source.uri; - let isObjectURL = false; - let hasAlpha = true; + } ); - if ( source.mimeType === 'image/jpeg' ) hasAlpha = false; + } - if ( source.bufferView !== undefined ) { + preload() { - // Load binary image data from bufferView, if provided. + this._initDecoder(); + + return this; - sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { + } - if ( source.mimeType === 'image/png' ) { + _initDecoder() { - // Inspect the PNG 'IHDR' chunk to determine whether the image could have an - // alpha channel. This check is conservative — the image could have an alpha - // channel with all values == 1, and the indexed type (colorType == 3) only - // sometimes contains alpha. - // - // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header - const colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false ); - hasAlpha = colorType === 6 || colorType === 4 || colorType === 3; + if ( this.decoderPending ) return this.decoderPending; - } + const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; + const librariesPending = []; - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: source.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; + if ( useJS ) { - } ); + librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - } else if ( source.uri === undefined ) { + } else { - throw new Error( 'THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView' ); + librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); + librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); } - return Promise.resolve( sourceURI ).then( function ( sourceURI ) { - - return new Promise( function ( resolve, reject ) { + this.decoderPending = Promise.all( librariesPending ) + .then( ( libraries ) => { - let onLoad = resolve; + const jsContent = libraries[ 0 ]; - if ( loader.isImageBitmapLoader === true ) { + if ( ! useJS ) { - onLoad = function ( imageBitmap ) { + this.decoderConfig.wasmBinary = libraries[ 1 ]; - resolve( new CanvasTexture( imageBitmap ) ); + } - }; + const fn = DRACOWorker.toString(); - } + const body = [ + '/* draco decoder */', + jsContent, + '', + '/* worker */', + fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) + ].join( '\n' ); - loader.load( resolveURL$1( sourceURI, options.path ), onLoad, undefined, reject ); + this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); } ); - } ).then( function ( texture ) { + return this.decoderPending; - // Clean up resources and configure Texture. + } - if ( isObjectURL === true ) { + _getWorker( taskID, taskCost ) { - URL.revokeObjectURL( sourceURI ); + return this._initDecoder().then( () => { - } + if ( this.workerPool.length < this.workerLimit ) { - texture.flipY = false; + const worker = new Worker( this.workerSourceURL ); - if ( textureDef.name ) texture.name = textureDef.name; + worker._callbacks = {}; + worker._taskCosts = {}; + worker._taskLoad = 0; - // When there is definitely no alpha channel in the texture, set RGBFormat to save space. - if ( ! hasAlpha ) texture.format = RGBFormat; + worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; + worker.onmessage = function ( e ) { - texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || RepeatWrapping; + const message = e.data; - parser.associations.set( texture, { - type: 'textures', - index: textureIndex - } ); + switch ( message.type ) { - return texture; + case 'decode': + worker._callbacks[ message.id ].resolve( message ); + break; - } ); + case 'error': + worker._callbacks[ message.id ].reject( message ); + break; - } + default: + console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef ) { + } - const parser = this; + }; - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { + this.workerPool.push( worker ); - // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured - // However, we will copy UV set 0 to UV set 1 on demand for aoMap - if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { + } else { - console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); + this.workerPool.sort( function ( a, b ) { + + return a._taskLoad > b._taskLoad ? - 1 : 1; + + } ); } - if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + const worker = this.workerPool[ this.workerPool.length - 1 ]; + worker._taskCosts[ taskID ] = taskCost; + worker._taskLoad += taskCost; + return worker; - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + } ); - if ( transform ) { + } - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); + _releaseTask( worker, taskID ) { - } + worker._taskLoad -= worker._taskCosts[ taskID ]; + delete worker._callbacks[ taskID ]; + delete worker._taskCosts[ taskID ]; - } + } - materialParams[ mapName ] = texture; + debug() { - } ); + console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); } - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { - - const geometry = mesh.geometry; - let material = mesh.material; + dispose() { - const useVertexTangents = geometry.attributes.tangent !== undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - const useSkinning = mesh.isSkinnedMesh === true; - const useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0; - const useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined; + for ( let i = 0; i < this.workerPool.length; ++ i ) { - if ( mesh.isPoints ) { + this.workerPool[ i ].terminate(); - const cacheKey = 'PointsMaterial:' + material.uuid; + } - let pointsMaterial = this.cache.get( cacheKey ); + this.workerPool.length = 0; - if ( ! pointsMaterial ) { + return this; - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px + } - this.cache.add( cacheKey, pointsMaterial ); + } - } + /* WEB WORKER */ - material = pointsMaterial; + function DRACOWorker() { - } else if ( mesh.isLine ) { + let decoderConfig; + let decoderPending; - const cacheKey = 'LineBasicMaterial:' + material.uuid; + onmessage = function ( e ) { - let lineMaterial = this.cache.get( cacheKey ); + const message = e.data; - if ( ! lineMaterial ) { + switch ( message.type ) { - lineMaterial = new LineBasicMaterial(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); + case 'init': + decoderConfig = message.decoderConfig; + decoderPending = new Promise( function ( resolve/*, reject*/ ) { - this.cache.add( cacheKey, lineMaterial ); + decoderConfig.onModuleLoaded = function ( draco ) { - } + // Module is Promise-like. Wrap before resolving to avoid loop. + resolve( { draco: draco } ); - material = lineMaterial; + }; - } + DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef - // Clone the material if it will be modified - if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) { + } ); + break; - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; + case 'decode': + const buffer = message.buffer; + const taskConfig = message.taskConfig; + decoderPending.then( ( module ) => { - if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; - if ( useSkinning ) cacheKey += 'skinning:'; - if ( useVertexTangents ) cacheKey += 'vertex-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - if ( useMorphTargets ) cacheKey += 'morph-targets:'; - if ( useMorphNormals ) cacheKey += 'morph-normals:'; + const draco = module.draco; + const decoder = new draco.Decoder(); + const decoderBuffer = new draco.DecoderBuffer(); + decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); - let cachedMaterial = this.cache.get( cacheKey ); + try { - if ( ! cachedMaterial ) { + const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); - cachedMaterial = material.clone(); + const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); - if ( useSkinning ) cachedMaterial.skinning = true; - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - if ( useMorphTargets ) cachedMaterial.morphTargets = true; - if ( useMorphNormals ) cachedMaterial.morphNormals = true; + if ( geometry.index ) buffers.push( geometry.index.array.buffer ); - if ( useVertexTangents ) { + self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); - cachedMaterial.vertexTangents = true; + } catch ( error ) { - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; + console.error( error ); - } + self.postMessage( { type: 'error', id: message.id, error: error.message } ); - this.cache.add( cacheKey, cachedMaterial ); + } finally { - this.associations.set( cachedMaterial, this.associations.get( material ) ); + draco.destroy( decoderBuffer ); + draco.destroy( decoder ); - } + } - material = cachedMaterial; + } ); + break; } - // workarounds for mesh and geometry + }; - if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { + function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { - geometry.setAttribute( 'uv2', geometry.attributes.uv ); + const attributeIDs = taskConfig.attributeIDs; + const attributeTypes = taskConfig.attributeTypes; - } + let dracoGeometry; + let decodingStatus; - mesh.material = material; + const geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); - } + if ( geometryType === draco.TRIANGULAR_MESH ) { - getMaterialType( /* materialIndex */ ) { + dracoGeometry = new draco.Mesh(); + decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - return MeshStandardMaterial; + } else if ( geometryType === draco.POINT_CLOUD ) { - } + dracoGeometry = new draco.PointCloud(); + decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { + } else { - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; + throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; + } - const pending = []; + if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { - if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { + throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); - const sgExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; - materialType = sgExtension.getMaterialType(); - pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); + } - } else if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + const geometry = { index: null, attributes: [] }; - const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); + // Gather all vertex attributes. + for ( const attributeName in attributeIDs ) { - } else { + const attributeType = self[ attributeTypes[ attributeName ] ]; - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material + let attribute; + let attributeID; - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; + // A Draco file may be created with default vertex attributes, whose attribute IDs + // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, + // a Draco file may contain a custom set of attributes, identified by known unique + // IDs. glTF files always do the latter, and `.drc` files typically do the former. + if ( taskConfig.useUniqueIDs ) { - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; + attributeID = attributeIDs[ attributeName ]; + attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + } else { - const array = metallicRoughness.baseColorFactor; + attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; + if ( attributeID === - 1 ) continue; - } + attribute = decoder.GetAttribute( dracoGeometry, attributeID ); - if ( metallicRoughness.baseColorTexture !== undefined ) { + } - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); + geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); - } + } - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + // Add index. + if ( geometryType === draco.TRIANGULAR_MESH ) { - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + geometry.index = decodeIndex( draco, decoder, dracoGeometry ); - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); + } - } + draco.destroy( dracoGeometry ); - materialType = this._invokeOne( function ( ext ) { + return geometry; - return ext.getMaterialType && ext.getMaterialType( materialIndex ); + } - } ); + function decodeIndex( draco, decoder, dracoGeometry ) { - pending.push( Promise.all( this._invokeAll( function ( ext ) { + const numFaces = dracoGeometry.num_faces(); + const numIndices = numFaces * 3; + const byteLength = numIndices * 4; - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); + const ptr = draco._malloc( byteLength ); + decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); + const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); + draco._free( ptr ); - } ) ) ); + return { array: index, itemSize: 1 }; - } + } - if ( materialDef.doubleSided === true ) { + function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { - materialParams.side = DoubleSide; + const numComponents = attribute.num_components(); + const numPoints = dracoGeometry.num_points(); + const numValues = numPoints * numComponents; + const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; + const dataType = getDracoDataType( draco, attributeType ); - } + const ptr = draco._malloc( byteLength ); + decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); + const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); + draco._free( ptr ); - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; + return { + name: attributeName, + array: array, + itemSize: numComponents + }; - if ( alphaMode === ALPHA_MODES.BLEND ) { + } - materialParams.transparent = true; + function getDracoDataType( draco, attributeType ) { - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; + switch ( attributeType ) { - } else { + case Float32Array: return draco.DT_FLOAT32; + case Int8Array: return draco.DT_INT8; + case Int16Array: return draco.DT_INT16; + case Int32Array: return draco.DT_INT32; + case Uint8Array: return draco.DT_UINT8; + case Uint16Array: return draco.DT_UINT16; + case Uint32Array: return draco.DT_UINT32; - materialParams.transparent = false; + } - if ( alphaMode === ALPHA_MODES.MASK ) { + } - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; + } - } + class GLTFLoader extends Loader { - } + constructor( manager ) { - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { + super( manager ); - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); + this.dracoLoader = null; + this.ktx2Loader = null; + this.meshoptDecoder = null; - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.normalScale = new Vector2( 1, - 1 ); + this.pluginCallbacks = []; - if ( materialDef.normalTexture.scale !== undefined ) { + this.register( function ( parser ) { - materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale ); + return new GLTFMaterialsClearcoatExtension( parser ); - } + } ); - } + this.register( function ( parser ) { - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { + return new GLTFTextureBasisUExtension( parser ); - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); + } ); - if ( materialDef.occlusionTexture.strength !== undefined ) { + this.register( function ( parser ) { - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; + return new GLTFTextureWebPExtension( parser ); - } + } ); - } + this.register( function ( parser ) { - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { + return new GLTFMaterialsSheenExtension( parser ); - materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); + } ); - } + this.register( function ( parser ) { - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { + return new GLTFMaterialsTransmissionExtension( parser ); - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); + } ); - } + this.register( function ( parser ) { - return Promise.all( pending ).then( function () { + return new GLTFMaterialsVolumeExtension( parser ); - let material; + } ); - if ( materialType === GLTFMeshStandardSGMaterial ) { + this.register( function ( parser ) { - material = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); + return new GLTFMaterialsIorExtension( parser ); - } else { + } ); - material = new materialType( materialParams ); + this.register( function ( parser ) { - } + return new GLTFMaterialsSpecularExtension( parser ); - if ( materialDef.name ) material.name = materialDef.name; + } ); - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if ( material.map ) material.map.encoding = sRGBEncoding; - if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding; + this.register( function ( parser ) { - assignExtrasToUserData( material, materialDef ); + return new GLTFLightsExtension( parser ); - parser.associations.set( material, { type: 'materials', index: materialIndex } ); + } ); - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); + this.register( function ( parser ) { - return material; + return new GLTFMeshoptCompression( parser ); } ); } - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { - - const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); - - let name = sanitizedName; + load( url, onLoad, onProgress, onError ) { - for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { + const scope = this; - name = sanitizedName + '_' + i; + let resourcePath; - } + if ( this.resourcePath !== '' ) { - this.nodeNamesUsed[ name ] = true; + resourcePath = this.resourcePath; - return name; + } else if ( this.path !== '' ) { - } + resourcePath = this.path; - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { + } else { - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; + resourcePath = LoaderUtils.extractUrlBase( url ); - function createDracoPrimitive( primitive ) { + } - return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { + // Tells the LoadingManager to track an extra item, which resolves after + // the model is fully loaded. This means the count of items loaded will + // be incorrect, but ensures manager.onLoad() does not fire early. + this.manager.itemStart( url ); - return addPrimitiveAttributes( geometry, primitive, parser ); + const _onError = function ( e ) { - } ); + if ( onError ) { - } + onError( e ); - const pending = []; + } else { - for ( let i = 0, il = primitives.length; i < il; i ++ ) { + console.error( e ); - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); + } - // See if we've already created this geometry - const cached = cache[ cacheKey ]; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - if ( cached ) { + }; - // Use the cached geometry if it exists - pending.push( cached.promise ); + const loader = new FileLoader( this.manager ); - } else { + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); - let geometryPromise; + loader.load( url, function ( data ) { - if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + try { - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); + scope.parse( data, resourcePath, function ( gltf ) { - } else { + onLoad( gltf ); - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); + scope.manager.itemEnd( url ); - } + }, _onError ); - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; + } catch ( e ) { - pending.push( geometryPromise ); + _onError( e ); } - } - - return Promise.all( pending ); + }, onProgress, _onError ); } - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { + setDRACOLoader( dracoLoader ) { - const parser = this; - const json = this.json; - const extensions = this.extensions; + this.dracoLoader = dracoLoader; + return this; - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; + } - const pending = []; + setDDSLoader() { - for ( let i = 0, il = primitives.length; i < il; i ++ ) { + throw new Error( - const material = primitives[ i ].material === undefined - ? createDefaultMaterial$1( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); + 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - pending.push( material ); + ); - } + } - pending.push( parser.loadGeometries( primitives ) ); + setKTX2Loader( ktx2Loader ) { - return Promise.all( pending ).then( function ( results ) { + this.ktx2Loader = ktx2Loader; + return this; - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; + } - const meshes = []; + setMeshoptDecoder( meshoptDecoder ) { - for ( let i = 0, il = geometries.length; i < il; i ++ ) { + this.meshoptDecoder = meshoptDecoder; + return this; - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; + } - // 1. create Mesh + register( callback ) { - let mesh; + if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - const material = materials[ i ]; + this.pluginCallbacks.push( callback ); - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || - primitive.mode === undefined ) { + } - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); + return this; - if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { + } - // we normalize floating point skin weight array to fix malformed assets (see #15319) - // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs - mesh.normalizeSkinWeights(); + unregister( callback ) { - } + if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); + } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + return this; - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); + } - } + parse( data, path, onLoad, onError ) { - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + let content; + const extensions = {}; + const plugins = {}; - mesh = new LineSegments( geometry, material ); + if ( typeof data === 'string' ) { - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + content = data; - mesh = new Line( geometry, material ); + } else { - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - mesh = new LineLoop( geometry, material ); + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + try { - mesh = new Points( geometry, material ); + extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); - } else { + } catch ( error ) { - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); + if ( onError ) onError( error ); + return; } - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { + content = extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content; - updateMorphTargets( mesh, meshDef ); + } else { - } + content = LoaderUtils.decodeText( new Uint8Array( data ) ); - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); + } - assignExtrasToUserData( mesh, meshDef ); + } - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); + const json = JSON.parse( content ); - parser.assignFinalMaterial( mesh ); + if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { - meshes.push( mesh ); + if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); + return; - } + } - if ( meshes.length === 1 ) { + const parser = new GLTFParser$1( json, { - return meshes[ 0 ]; + path: path || this.resourcePath || '', + crossOrigin: this.crossOrigin, + requestHeader: this.requestHeader, + manager: this.manager, + ktx2Loader: this.ktx2Loader, + meshoptDecoder: this.meshoptDecoder - } + } ); - const group = new Group(); + parser.fileLoader.setRequestHeader( this.requestHeader ); - for ( let i = 0, il = meshes.length; i < il; i ++ ) { + for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - group.add( meshes[ i ] ); + const plugin = this.pluginCallbacks[ i ]( parser ); + plugins[ plugin.name ] = plugin; - } + // Workaround to avoid determining as unknown extension + // in addUnknownExtensionsToUserData(). + // Remove this workaround if we move all the existing + // extension handlers to plugin system + extensions[ plugin.name ] = true; - return group; + } - } ); + if ( json.extensionsUsed ) { - } + for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { + const extensionName = json.extensionsUsed[ i ]; + const extensionsRequired = json.extensionsRequired || []; - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; + switch ( extensionName ) { - if ( ! params ) { + case EXTENSIONS$1.KHR_MATERIALS_UNLIT: + extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); + break; - console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); - return; + case EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: + extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); + break; - } + case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: + extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); + break; - if ( cameraDef.type === 'perspective' ) { + case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: + extensions[ extensionName ] = new GLTFTextureTransformExtension(); + break; - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); + case EXTENSIONS$1.KHR_MESH_QUANTIZATION: + extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); + break; - } else if ( cameraDef.type === 'orthographic' ) { + default: - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); + if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { - } + console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); + } - assignExtrasToUserData( camera, cameraDef ); + } - return Promise.resolve( camera ); + } + + } + + parser.setExtensions( extensions ); + parser.setPlugins( plugins ); + parser.parse( onLoad, onError ); } - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { + parseAsync( data, path ) { - const skinDef = this.json.skins[ skinIndex ]; + const scope = this; - const skinEntry = { joints: skinDef.joints }; + return new Promise( function ( resolve, reject ) { - if ( skinDef.inverseBindMatrices === undefined ) { + scope.parse( data, path, resolve, reject ); - return Promise.resolve( skinEntry ); + } ); - } + } - return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { + } - skinEntry.inverseBindMatrices = accessor; + /* GLTFREGISTRY */ - return skinEntry; + function GLTFRegistry$1() { - } ); + let objects = {}; - } + return { - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { - - const json = this.json; - - const animationDef = json.animations[ animationIndex ]; - - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; - - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { + get: function ( key ) { - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; + return objects[ key ]; - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); + }, - } + add: function ( key, object ) { - return Promise.all( [ + objects[ key ] = object; - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) + }, - ] ).then( function ( dependencies ) { + remove: function ( key ) { - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; + delete objects[ key ]; - const tracks = []; + }, - for ( let i = 0, il = nodes.length; i < il; i ++ ) { + removeAll: function () { - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; + objects = {}; - if ( node === undefined ) continue; + } - node.updateMatrix(); - node.matrixAutoUpdate = true; + }; - let TypedKeyframeTrack; + } - switch ( PATH_PROPERTIES$1[ target.path ] ) { + /*********************************/ + /********** EXTENSIONS ***********/ + /*********************************/ - case PATH_PROPERTIES$1.weights: + const EXTENSIONS$1 = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', + KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', + KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', + KHR_MATERIALS_IOR: 'KHR_materials_ior', + KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', + KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', + KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', + KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', + KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', + KHR_MATERIALS_VOLUME: 'KHR_materials_volume', + KHR_TEXTURE_BASISU: 'KHR_texture_basisu', + KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', + KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', + EXT_TEXTURE_WEBP: 'EXT_texture_webp', + EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' + }; - TypedKeyframeTrack = NumberKeyframeTrack; - break; + /** + * Punctual Lights Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual + */ + class GLTFLightsExtension { - case PATH_PROPERTIES$1.rotation: + constructor( parser ) { - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; + this.parser = parser; + this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: - default: + // Object3D instance caches + this.cache = { refs: {}, uses: {} }; - TypedKeyframeTrack = VectorKeyframeTrack; - break; + } - } + _markDefs() { - const targetName = node.name ? node.name : node.uuid; + const parser = this.parser; + const nodeDefs = this.parser.json.nodes || []; - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : InterpolateLinear; + for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - const targetNames = []; + const nodeDef = nodeDefs[ nodeIndex ]; - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + if ( nodeDef.extensions + && nodeDef.extensions[ this.name ] + && nodeDef.extensions[ this.name ].light !== undefined ) { - // Node may be a Group (glTF mesh with several primitives) or a Mesh. - node.traverse( function ( object ) { + parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - if ( object.isMesh === true && object.morphTargetInfluences ) { + } - targetNames.push( object.name ? object.name : object.uuid ); + } - } + } - } ); + _loadLight( lightIndex ) { - } else { + const parser = this.parser; + const cacheKey = 'light:' + lightIndex; + let dependency = parser.cache.get( cacheKey ); - targetNames.push( targetName ); + if ( dependency ) return dependency; - } + const json = parser.json; + const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; + const lightDefs = extensions.lights || []; + const lightDef = lightDefs[ lightIndex ]; + let lightNode; - let outputArray = outputAccessor.array; + const color = new Color( 0xffffff ); - if ( outputAccessor.normalized ) { + if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); + const range = lightDef.range !== undefined ? lightDef.range : 0; - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { + switch ( lightDef.type ) { - scaled[ j ] = outputArray[ j ] * scale; + case 'directional': + lightNode = new DirectionalLight( color ); + lightNode.target.position.set( 0, 0, - 1 ); + lightNode.add( lightNode.target ); + break; - } + case 'point': + lightNode = new PointLight( color ); + lightNode.distance = range; + break; - outputArray = scaled; + case 'spot': + lightNode = new SpotLight( color ); + lightNode.distance = range; + // Handle spotlight properties. + lightDef.spot = lightDef.spot || {}; + lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; + lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; + lightNode.angle = lightDef.spot.outerConeAngle; + lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; + lightNode.target.position.set( 0, 0, - 1 ); + lightNode.add( lightNode.target ); + break; - } + default: + throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { + } - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); + // Some lights (e.g. spot) default to a position other than the origin. Reset the position + // here, because node-level parsing will only override position if explicitly specified. + lightNode.position.set( 0, 0, 0 ); - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { + lightNode.decay = 2; - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { + if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. + lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result ); + dependency = Promise.resolve( lightNode ); - }; + parser.cache.add( cacheKey, dependency ); - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; + return dependency; - } + } - tracks.push( track ); + createNodeAttachment( nodeIndex ) { - } + const self = this; + const parser = this.parser; + const json = parser.json; + const nodeDef = json.nodes[ nodeIndex ]; + const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; + const lightIndex = lightDef.light; - } + if ( lightIndex === undefined ) return null; - const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; + return this._loadLight( lightIndex ).then( function ( light ) { - return new AnimationClip( name, undefined, tracks ); + return parser._getNodeRef( self.cache, lightIndex, light ); } ); } - createNodeMesh( nodeIndex ) { + } - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; + /** + * Unlit Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit + */ + class GLTFMaterialsUnlitExtension { - if ( nodeDef.mesh === undefined ) return null; + constructor() { - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { + this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); + } - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { + getMaterialType() { - node.traverse( function ( o ) { + return MeshBasicMaterial; - if ( ! o.isMesh ) return; + } - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { + extendParams( materialParams, materialDef, parser ) { - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; + const pending = []; - } + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; - } ); + const metallicRoughness = materialDef.pbrMetallicRoughness; - } + if ( metallicRoughness ) { - return node; + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - } ); + const array = metallicRoughness.baseColorFactor; - } + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { + } - const json = this.json; - const extensions = this.extensions; - const parser = this; + if ( metallicRoughness.baseColorTexture !== undefined ) { - const nodeDef = json.nodes[ nodeIndex ]; + pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) ); - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; + } - return ( function () { + } - const pending = []; + return Promise.all( pending ); - const meshPromise = parser._invokeOne( function ( ext ) { + } - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); + } - } ); + /** + * Clearcoat Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + class GLTFMaterialsClearcoatExtension { - if ( meshPromise ) { + constructor( parser ) { - pending.push( meshPromise ); + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; - } + } - if ( nodeDef.camera !== undefined ) { + getMaterialType( materialIndex ) { - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - } ) ); + return MeshPhysicalMaterial; - } + } - parser._invokeAll( function ( ext ) { + extendMaterialParams( materialIndex, materialParams ) { - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } ).forEach( function ( promise ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - pending.push( promise ); + return Promise.resolve(); - } ); + } - return Promise.all( pending ); + const pending = []; - }() ).then( function ( objects ) { + const extension = materialDef.extensions[ this.name ]; - let node; + if ( extension.clearcoatFactor !== undefined ) { - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { + materialParams.clearcoat = extension.clearcoatFactor; - node = new Bone(); + } - } else if ( objects.length > 1 ) { + if ( extension.clearcoatTexture !== undefined ) { - node = new Group(); + pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); - } else if ( objects.length === 1 ) { + } - node = objects[ 0 ]; + if ( extension.clearcoatRoughnessFactor !== undefined ) { - } else { + materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; - node = new Object3D(); + } - } + if ( extension.clearcoatRoughnessTexture !== undefined ) { - if ( node !== objects[ 0 ] ) { + pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - for ( let i = 0, il = objects.length; i < il; i ++ ) { + } - node.add( objects[ i ] ); + if ( extension.clearcoatNormalTexture !== undefined ) { - } + pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - } + if ( extension.clearcoatNormalTexture.scale !== undefined ) { - if ( nodeDef.name ) { + const scale = extension.clearcoatNormalTexture.scale; - node.userData.name = nodeDef.name; - node.name = nodeName; + materialParams.clearcoatNormalScale = new Vector2( scale, scale ); } - assignExtrasToUserData( node, nodeDef ); + } - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); + return Promise.all( pending ); - if ( nodeDef.matrix !== undefined ) { + } - const matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); + } - } else { + /** + * Sheen Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen + */ + class GLTFMaterialsSheenExtension { - if ( nodeDef.translation !== undefined ) { + constructor( parser ) { - node.position.fromArray( nodeDef.translation ); + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_SHEEN; - } + } - if ( nodeDef.rotation !== undefined ) { + getMaterialType( materialIndex ) { - node.quaternion.fromArray( nodeDef.rotation ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - if ( nodeDef.scale !== undefined ) { + return MeshPhysicalMaterial; - node.scale.fromArray( nodeDef.scale ); + } - } + extendMaterialParams( materialIndex, materialParams ) { - } + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - parser.associations.set( node, { type: 'nodes', index: nodeIndex } ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - return node; + return Promise.resolve(); - } ); + } - } + const pending = []; - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { + materialParams.sheenColor = new Color( 0, 0, 0 ); + materialParams.sheenRoughness = 0; + materialParams.sheen = 1; - const json = this.json; - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; + const extension = materialDef.extensions[ this.name ]; - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new Group(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); + if ( extension.sheenColorFactor !== undefined ) { - assignExtrasToUserData( scene, sceneDef ); + materialParams.sheenColor.fromArray( extension.sheenColorFactor ); - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); + } - const nodeIds = sceneDef.nodes || []; + if ( extension.sheenRoughnessFactor !== undefined ) { - const pending = []; + materialParams.sheenRoughness = extension.sheenRoughnessFactor; - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { + } + + if ( extension.sheenColorTexture !== undefined ) { - pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) ); + pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, sRGBEncoding ) ); } - return Promise.all( pending ).then( function () { + if ( extension.sheenRoughnessTexture !== undefined ) { - return scene; + pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); - } ); + } + + return Promise.all( pending ); } } - function buildNodeHierachy( nodeId, parentObject, json, parser ) { + /** + * Transmission Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission + * Draft: https://github.com/KhronosGroup/glTF/pull/1698 + */ + class GLTFMaterialsTransmissionExtension { - const nodeDef = json.nodes[ nodeId ]; + constructor( parser ) { - return parser.getDependency( 'node', nodeId ).then( function ( node ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; - if ( nodeDef.skin === undefined ) return node; + } - // build skeleton here as well + getMaterialType( materialIndex ) { - let skinEntry; + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - skinEntry = skin; + return MeshPhysicalMaterial; - const pendingJoints = []; + } - for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { + extendMaterialParams( materialIndex, materialParams ) { - pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - return Promise.all( pendingJoints ); + return Promise.resolve(); - } ).then( function ( jointNodes ) { + } - node.traverse( function ( mesh ) { + const pending = []; - if ( ! mesh.isMesh ) return; + const extension = materialDef.extensions[ this.name ]; - const bones = []; - const boneInverses = []; + if ( extension.transmissionFactor !== undefined ) { - for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { + materialParams.transmission = extension.transmissionFactor; - const jointNode = jointNodes[ j ]; + } - if ( jointNode ) { + if ( extension.transmissionTexture !== undefined ) { - bones.push( jointNode ); + pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - const mat = new Matrix4(); + } - if ( skinEntry.inverseBindMatrices !== undefined ) { + return Promise.all( pending ); - mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); + } - } + } - boneInverses.push( mat ); + /** + * Materials Volume Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume + */ + class GLTFMaterialsVolumeExtension { - } else { + constructor( parser ) { - console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_VOLUME; - } + } - } + getMaterialType( materialIndex ) { - mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - return node; + return MeshPhysicalMaterial; - } ); + } - } ).then( function ( node ) { + extendMaterialParams( materialIndex, materialParams ) { - // build node hierachy + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - parentObject.add( node ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - const pending = []; + return Promise.resolve(); - if ( nodeDef.children ) { + } - const children = nodeDef.children; + const pending = []; - for ( let i = 0, il = children.length; i < il; i ++ ) { + const extension = materialDef.extensions[ this.name ]; - const child = children[ i ]; - pending.push( buildNodeHierachy( child, node, json, parser ) ); + materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - } + if ( extension.thicknessTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); } + materialParams.attenuationDistance = extension.attenuationDistance || 0; + + const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; + materialParams.attenuationColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); + return Promise.all( pending ); - } ); + } } /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser + * Materials ior Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior */ - function computeBounds( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; + class GLTFMaterialsIorExtension { - const box = new Box3(); - - if ( attributes.POSITION !== undefined ) { + constructor( parser ) { - const accessor = parser.json.accessors[ attributes.POSITION ]; + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_IOR; - const min = accessor.min; - const max = accessor.max; + } - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + getMaterialType( materialIndex ) { - if ( min !== undefined && max !== undefined ) { + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - if ( accessor.normalized ) { + return MeshPhysicalMaterial; - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); + } - } + extendMaterialParams( materialIndex, materialParams ) { - } else { + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - return; + return Promise.resolve(); } - } else { + const extension = materialDef.extensions[ this.name ]; - return; + materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; + + return Promise.resolve(); } - const targets = primitiveDef.targets; + } - if ( targets !== undefined ) { + /** + * Materials specular Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular + */ + class GLTFMaterialsSpecularExtension { - const maxDisplacement = new Vector3(); - const vector = new Vector3(); + constructor( parser ) { - for ( let i = 0, il = targets.length; i < il; i ++ ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_SPECULAR; - const target = targets[ i ]; + } - if ( target.POSITION !== undefined ) { + getMaterialType( materialIndex ) { - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - if ( min !== undefined && max !== undefined ) { + return MeshPhysicalMaterial; - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); + } + extendMaterialParams( materialIndex, materialParams ) { - if ( accessor.normalized ) { + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - } + return Promise.resolve(); - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); + } - } else { + const pending = []; - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + const extension = materialDef.extensions[ this.name ]; - } + materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - } + if ( extension.specularTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); } - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); + const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; + materialParams.specularColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); - } + if ( extension.specularColorTexture !== undefined ) { - geometry.boundingBox = box; + pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, sRGBEncoding ) ); - const sphere = new Sphere(); + } - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; + return Promise.all( pending ); - geometry.boundingSphere = sphere; + } } /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} + * BasisU Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu */ - function addPrimitiveAttributes( geometry, primitiveDef, parser ) { - - const attributes = primitiveDef.attributes; - - const pending = []; - - function assignAttributeAccessor( accessorIndex, attributeName ) { - - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { + class GLTFTextureBasisUExtension { - geometry.setAttribute( attributeName, accessor ); + constructor( parser ) { - } ); + this.parser = parser; + this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; } - for ( const gltfAttributeName in attributes ) { + loadTexture( textureIndex ) { - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + const parser = this.parser; + const json = parser.json; - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; + const textureDef = json.textures[ textureIndex ]; - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); + if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - } + return null; - if ( primitiveDef.indices !== undefined && ! geometry.index ) { + } - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { + const extension = textureDef.extensions[ this.name ]; + const loader = parser.options.ktx2Loader; - geometry.setIndex( accessor ); + if ( ! loader ) { - } ); + if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - pending.push( accessor ); + throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - } + } else { - assignExtrasToUserData( geometry, primitiveDef ); + // Assumes that the extension is optional and that a fallback texture is present + return null; - computeBounds( geometry, primitiveDef, parser ); + } - return Promise.all( pending ).then( function () { + } - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; + return parser.loadTextureImage( textureIndex, extension.source, loader ); - } ); + } } /** - * @param {BufferGeometry} geometry - * @param {Number} drawMode - * @return {BufferGeometry} + * WebP Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp */ - function toTrianglesDrawMode( geometry, drawMode ) { + class GLTFTextureWebPExtension { - let index = geometry.getIndex(); + constructor( parser ) { - // generate index if not present + this.parser = parser; + this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; + this.isSupported = null; - if ( index === null ) { + } - const indices = []; + loadTexture( textureIndex ) { - const position = geometry.getAttribute( 'position' ); + const name = this.name; + const parser = this.parser; + const json = parser.json; - if ( position !== undefined ) { + const textureDef = json.textures[ textureIndex ]; - for ( let i = 0; i < position.count; i ++ ) { + if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { - indices.push( i ); + return null; - } + } - geometry.setIndex( indices ); - index = geometry.getIndex(); + const extension = textureDef.extensions[ name ]; + const source = json.images[ extension.source ]; - } else { + let loader = parser.textureLoader; + if ( source.uri ) { - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; + const handler = parser.options.manager.getHandler( source.uri ); + if ( handler !== null ) loader = handler; } - } + return this.detectSupport().then( function ( isSupported ) { - // + if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - const numberOfTriangles = index.count - 2; - const newIndices = []; + if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - if ( drawMode === TriangleFanDrawMode ) { + throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - // gl.TRIANGLE_FAN + } - for ( let i = 1; i <= numberOfTriangles; i ++ ) { + // Fall back to PNG or JPEG. + return parser.loadTexture( textureIndex ); - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); + } ); - } + } - } else { + detectSupport() { - // gl.TRIANGLE_STRIP + if ( ! this.isSupported ) { - for ( let i = 0; i < numberOfTriangles; i ++ ) { + this.isSupported = new Promise( function ( resolve ) { - if ( i % 2 === 0 ) { + const image = new Image(); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); + // Lossy test image. Support for lossy images doesn't guarantee support for all + // WebP images, unfortunately. + image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; + image.onload = image.onerror = function () { - } else { + resolve( image.height === 1 ); - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); + }; - } + } ); } + return this.isSupported; + } - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { + } - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); + /** + * meshopt BufferView Compression Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression + */ + class GLTFMeshoptCompression { + + constructor( parser ) { + + this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; + this.parser = parser; } - // build final geometry + loadBufferView( index ) { - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); + const json = this.parser.json; + const bufferView = json.bufferViews[ index ]; - return newGeometry; + if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - } + const extensionDef = bufferView.extensions[ this.name ]; - class VRButton { + const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); + const decoder = this.parser.options.meshoptDecoder; - static createButton( renderer, options ) { + if ( ! decoder || ! decoder.supported ) { - if ( options ) { + if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - console.error( 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.' ); + throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - } + } else { - const button = document.createElement( 'button' ); + // Assumes that the extension is optional and that fallback buffer data is present + return null; - function showEnterVR( /*device*/ ) { + } - let currentSession = null; + } - async function onSessionStarted( session ) { + return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { - session.addEventListener( 'end', onSessionEnded ); + const byteOffset = extensionDef.byteOffset || 0; + const byteLength = extensionDef.byteLength || 0; - await renderer.xr.setSession( session ); - button.textContent = 'EXIT VR'; + const count = extensionDef.count; + const stride = extensionDef.byteStride; - currentSession = session; + const result = new ArrayBuffer( count * stride ); + const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); - } + decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); + return result; - function onSessionEnded( /*event*/ ) { + } ); - currentSession.removeEventListener( 'end', onSessionEnded ); + } else { - button.textContent = 'ENTER VR'; + return null; - currentSession = null; + } - } + } - // + } - button.style.display = ''; + /* BINARY EXTENSION */ + const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; + const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; + const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - button.style.cursor = 'pointer'; - button.style.left = 'calc(50% - 50px)'; - button.style.width = '100px'; + class GLTFBinaryExtension$1 { - button.textContent = 'ENTER VR'; + constructor( data ) { - button.onmouseenter = function () { + this.name = EXTENSIONS$1.KHR_BINARY_GLTF; + this.content = null; + this.body = null; - button.style.opacity = '1.0'; + const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); - }; + this.header = { + magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; - button.onmouseleave = function () { + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - button.style.opacity = '0.5'; + throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - }; + } else if ( this.header.version < 2.0 ) { - button.onclick = function () { + throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - if ( currentSession === null ) { + } - // WebXR's requestReferenceSpace only works if the corresponding feature - // was requested at session creation time. For simplicity, just ask for - // the interesting ones as optional features, but be aware that the - // requestReferenceSpace call will fail if it turns out to be unavailable. - // ('local' is always available for immersive sessions and doesn't need to - // be requested separately.) + const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; + const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); + let chunkIndex = 0; - const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking' ] }; - navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted ); + while ( chunkIndex < chunkContentsLength ) { - } else { + const chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; - currentSession.end(); + const chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; - } + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - }; + const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); + this.content = LoaderUtils.decodeText( contentArray ); - } + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { - function disableButton() { + const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); - button.style.display = ''; + } - button.style.cursor = 'auto'; - button.style.left = 'calc(50% - 75px)'; - button.style.width = '150px'; + // Clients must ignore chunks with unknown types. - button.onmouseenter = null; - button.onmouseleave = null; + chunkIndex += chunkLength; - button.onclick = null; + } + + if ( this.content === null ) { + + throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); } - function showWebXRNotFound() { + } - disableButton(); + } - button.textContent = 'VR NOT SUPPORTED'; + /** + * DRACO Mesh Compression Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression + */ + class GLTFDracoMeshCompressionExtension { - } + constructor( json, dracoLoader ) { - function stylizeElement( element ) { + if ( ! dracoLoader ) { - element.style.position = 'absolute'; - element.style.bottom = '20px'; - element.style.padding = '12px 6px'; - element.style.border = '1px solid #fff'; - element.style.borderRadius = '4px'; - element.style.background = 'rgba(0,0,0,0.1)'; - element.style.color = '#fff'; - element.style.font = 'normal 13px sans-serif'; - element.style.textAlign = 'center'; - element.style.opacity = '0.5'; - element.style.outline = 'none'; - element.style.zIndex = '999'; + throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); } - if ( 'xr' in navigator ) { + this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; + this.json = json; + this.dracoLoader = dracoLoader; + this.dracoLoader.preload(); - button.id = 'VRButton'; - button.style.display = 'none'; + } - stylizeElement( button ); + decodePrimitive( primitive, parser ) { - navigator.xr.isSessionSupported( 'immersive-vr' ).then( function ( supported ) { + const json = this.json; + const dracoLoader = this.dracoLoader; + const bufferViewIndex = primitive.extensions[ this.name ].bufferView; + const gltfAttributeMap = primitive.extensions[ this.name ].attributes; + const threeAttributeMap = {}; + const attributeNormalizedMap = {}; + const attributeTypeMap = {}; - supported ? showEnterVR() : showWebXRNotFound(); + for ( const attributeName in gltfAttributeMap ) { - } ); + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - return button; + threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; - } else { + } - const message = document.createElement( 'a' ); + for ( const attributeName in primitive.attributes ) { - if ( window.isSecureContext === false ) { + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); - message.href = document.location.href.replace( /^http:/, 'https:' ); - message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message + if ( gltfAttributeMap[ attributeName ] !== undefined ) { - } else { + const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; + const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; - message.href = 'https://immersiveweb.dev/'; - message.innerHTML = 'WEBXR NOT AVAILABLE'; + attributeTypeMap[ threeAttributeName ] = componentType; + attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; } - message.style.left = 'calc(50% - 90px)'; - message.style.width = '180px'; - message.style.textDecoration = 'none'; + } - stylizeElement( message ); + return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { - return message; + return new Promise( function ( resolve ) { - } + dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { + + for ( const attributeName in geometry.attributes ) { + + const attribute = geometry.attributes[ attributeName ]; + const normalized = attributeNormalizedMap[ attributeName ]; + + if ( normalized !== undefined ) attribute.normalized = normalized; + + } + + resolve( geometry ); + + }, threeAttributeMap, attributeTypeMap ); + + } ); + + } ); } } - /*! - * three-icosa - * https://github.com/icosa-gallery/three-icosa - * Copyright (c) 2021-2022 Icosa Gallery - * Released under the Apache 2.0 Licence. + /** + * Texture Transform Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform */ + class GLTFTextureTransformExtension { - // Copyright 2021-2022 Icosa Gallery + constructor() { - class TiltShaderLoader extends Loader { - constructor( manager ) { - super( manager ); - - this.loadedMaterials = {}; - } - - async load(brushName, onLoad, onProgress, onError ) { - const scope = this; + this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; - const isAlreadyLoaded = this.loadedMaterials[brushName]; - - if (isAlreadyLoaded !== undefined) { - onLoad( scope.parse( isAlreadyLoaded ) ); - return; - } - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'text' ); - loader.setWithCredentials( this.withCredentials ); + } - const textureLoader = new TextureLoader(this.manager); - textureLoader.setPath(this.path); - textureLoader.setWithCredentials( this.withCredentials ); + extendTexture( texture, transform ) { - const materialParams = tiltBrushMaterialParams[brushName]; + if ( transform.texCoord !== undefined ) { - materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); - materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); - if (materialParams.uniforms.u_MainTex) { - const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); - mainTex.name = `${brushName}_MainTex`; - mainTex.wrapS = RepeatWrapping; - mainTex.wrapT = RepeatWrapping; - mainTex.flipY = false; - materialParams.uniforms.u_MainTex.value = mainTex; - } + } - if (materialParams.uniforms.u_BumpMap) { - const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); - bumpMap.name = `${brushName}_BumpMap`; - bumpMap.wrapS = RepeatWrapping; - bumpMap.wrapT = RepeatWrapping; - bumpMap.flipY = false; - materialParams.uniforms.u_BumpMap.value = bumpMap; - } + if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) { - if (materialParams.uniforms.u_AlphaMask) { - const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); - alphaMask.name = `${brushName}_AlphaMask`; - alphaMask.wrapS = RepeatWrapping; - alphaMask.wrapT = RepeatWrapping; - alphaMask.flipY = false; - materialParams.uniforms.u_AlphaMask.value = alphaMask; - } + // See https://github.com/mrdoob/three.js/issues/21819. + return texture; - // inject three.js lighting uniforms - for(var lightType in UniformsLib.lights) - { - materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; - } + } - let rawMaterial = new RawShaderMaterial(materialParams); - this.loadedMaterials[brushName] = rawMaterial; - onLoad( scope.parse( rawMaterial ) ); - } + texture = texture.clone(); - parse( rawMaterial ) { - return rawMaterial; - } + if ( transform.offset !== undefined ) { - lookupMaterial(nameOrGuid) { - const name = this.lookupMaterialName(nameOrGuid); - return tiltBrushMaterialParams[name]; - } + texture.offset.fromArray( transform.offset ); - lookupMaterialName(nameOrGuid) { - switch(nameOrGuid) { - case "BlocksBasic:": - case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": - return "BlocksBasic"; + } - case "BlocksGem": - case "232998f8-d357-47a2-993a-53415df9be10": - return "BlocksGem"; + if ( transform.rotation !== undefined ) { - case "BlocksGlass": - case "3d813d82-5839-4450-8ddc-8e889ecd96c7": - return "BlocksGlass"; + texture.rotation = transform.rotation; - case "Bubbles": - case "89d104cd-d012-426b-b5b3-bbaee63ac43c": - return "Bubbles"; + } - case "CelVinyl": - case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": - return "CelVinyl"; + if ( transform.scale !== undefined ) { - case "ChromaticWave": - case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": - return "ChromaticWave"; + texture.repeat.fromArray( transform.scale ); - case "CoarseBristles": - case "1161af82-50cf-47db-9706-0c3576d43c43": - case "79168f10-6961-464a-8be1-57ed364c5600": - return "CoarseBristles"; - - case "Comet": - case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": - return "Comet"; - - case "DiamondHull": - case "c8313697-2563-47fc-832e-290f4c04b901": - return "DiamondHull"; - - case "Disco": - case "4391aaaa-df73-4396-9e33-31e4e4930b27": - return "Disco"; - - case "DotMarker": - case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": - return "DotMarker"; - - case "Dots": - case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": - return "Dots"; + } - case "DoubleTaperedFlat": - case "0d3889f3-3ede-470c-8af4-f44813306126": - return "DoubleTaperedFlat"; - - case "DoubleTaperedMarker": - case "0d3889f3-3ede-470c-8af4-de4813306126": - return "DoubleTaperedMarker"; - - case "DuctTape": - case "d0262945-853c-4481-9cbd-88586bed93cb": - case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": - return "DuctTape"; - - case "Electricity": - case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": - return "Electricity"; + texture.needsUpdate = true; - case "Embers": - case "02ffb866-7fb2-4d15-b761-1012cefb1360": - return "Embers"; - - case "EnvironmentDiffuse": - case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": - return "EnvironmentDiffuse"; - - case "EnvironmentDiffuseLightMap": - case "d01d9d6c-9a61-4aba-8146-5891fafb013b": - return "EnvironmentDiffuseLightMap"; + return texture; - case "Fire": - case "cb92b597-94ca-4255-b017-0e3f42f12f9e": - return "Fire"; + } - case "2d35bcf0-e4d8-452c-97b1-3311be063130": - case "280c0a7a-aad8-416c-a7d2-df63d129ca70": - case "55303bc4-c749-4a72-98d9-d23e68e76e18": - case "Flat": - return "Flat"; - - case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": - case "geometry_Highlighter": - return "Highlighter"; - - case "Hypercolor": - case "dce872c2-7b49-4684-b59b-c45387949c5c": - case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": - return "Hypercolor"; - - case "HyperGrid": - case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": - return "HyperGrid"; + } - case "Icing": - case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": - return "Icing"; - - case "Ink": - case "f5c336cf-5108-4b40-ade9-c687504385ab": - case "c0012095-3ffd-4040-8ee1-fc180d346eaa": - return "Ink"; + /** + * Specular-Glossiness Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness + */ - case "Leaves": - case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": - case "ea19de07-d0c0-4484-9198-18489a3c1487": - return "Leaves"; + /** + * A sub class of StandardMaterial with some of the functionality + * changed via the `onBeforeCompile` callback + * @pailhead + */ + class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { - case "Light": - case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": - return "Light"; + constructor( params ) { - case "LightWire": - case "4391aaaa-df81-4396-9e33-31e4e4930b27": - return "LightWire"; - - case "Lofted": - case "d381e0f5-3def-4a0d-8853-31e9200bcbda": - return "Lofted"; + super(); - case "Marker": - case "429ed64a-4e97-4466-84d3-145a861ef684": - return "Marker"; - - case "MatteHull": - case "79348357-432d-4746-8e29-0e25c112e3aa": - return "MatteHull"; + this.isGLTFSpecularGlossinessMaterial = true; - case "NeonPulse": - case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": - return "NeonPulse"; + //various chunks that need replacing + const specularMapParsFragmentChunk = [ + '#ifdef USE_SPECULARMAP', + ' uniform sampler2D specularMap;', + '#endif' + ].join( '\n' ); - case "OilPaint": - case "f72ec0e7-a844-4e38-82e3-140c44772699": - case "c515dad7-4393-4681-81ad-162ef052241b": - return "OilPaint"; + const glossinessMapParsFragmentChunk = [ + '#ifdef USE_GLOSSINESSMAP', + ' uniform sampler2D glossinessMap;', + '#endif' + ].join( '\n' ); - case "Paper": - case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": - case "759f1ebd-20cd-4720-8d41-234e0da63716": - return "Paper"; - - case "PbrTemplate": - case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": - return "PbrTemplate"; - - case "PbrTransparentTemplate": - case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": - return "PbrTransparentTemplate"; - - case "Petal": - case "e0abbc80-0f80-e854-4970-8924a0863dcc": - return "Petal"; + const specularMapFragmentChunk = [ + 'vec3 specularFactor = specular;', + '#ifdef USE_SPECULARMAP', + ' vec4 texelSpecular = texture2D( specularMap, vUv );', + ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' specularFactor *= texelSpecular.rgb;', + '#endif' + ].join( '\n' ); - case "Plasma": - case "c33714d1-b2f9-412e-bd50-1884c9d46336": - return "Plasma"; - - case "Rainbow": - case "ad1ad437-76e2-450d-a23a-e17f8310b960": - return "Rainbow"; + const glossinessMapFragmentChunk = [ + 'float glossinessFactor = glossiness;', + '#ifdef USE_GLOSSINESSMAP', + ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', + ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' glossinessFactor *= texelGlossiness.a;', + '#endif' + ].join( '\n' ); - case "ShinyHull": - case "faaa4d44-fcfb-4177-96be-753ac0421ba3": - return "ShinyHull"; + const lightPhysicalFragmentChunk = [ + 'PhysicalMaterial material;', + 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', + 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', + 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', + 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', + 'material.roughness += geometryRoughness;', + 'material.roughness = min( material.roughness, 1.0 );', + 'material.specularColor = specularFactor;', + ].join( '\n' ); - case "Smoke": - case "70d79cca-b159-4f35-990c-f02193947fe8": - return "Smoke"; - - case "Snow": - case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": - return "Snow"; + const uniforms = { + specular: { value: new Color().setHex( 0xffffff ) }, + glossiness: { value: 1 }, + specularMap: { value: null }, + glossinessMap: { value: null } + }; - case "SoftHighlighter": - case "accb32f5-4509-454f-93f8-1df3fd31df1b": - return "SoftHighlighter"; - - case "Spikes": - case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": - return "Spikes"; - - case "Splatter": - case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": - case "7a1c8107-50c5-4b70-9a39-421576d6617e": - return "Splatter"; - - case "Stars": - case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": - return "Stars"; + this._extraUniforms = uniforms; - case "Streamers": - case "44bb800a-fbc3-4592-8426-94ecb05ddec3": - return "Streamers"; - - case "Taffy": - case "0077f88c-d93a-42f3-b59b-b31c50cdb414": - return "Taffy"; + this.onBeforeCompile = function ( shader ) { - case "TaperedFlat": - case "b468c1fb-f254-41ed-8ec9-57030bc5660c": - case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": - return "TaperedFlat"; + for ( const uniformName in uniforms ) { - case "TaperedMarker": - case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": - case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": - return "TaperedMarker"; + shader.uniforms[ uniformName ] = uniforms[ uniformName ]; - case "ThickPaint": - case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": - case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": - return "ThickPaint"; - - case "Toon": - case "4391385a-df73-4396-9e33-31e4e4930b27": - return "Toon"; + } - case "UnlitHull": - case "a8fea537-da7c-4d4b-817f-24f074725d6d": - return "UnlitHull"; - - case "VelvetInk": - case "d229d335-c334-495a-a801-660ac8a87360": - return "VelvetInk"; + shader.fragmentShader = shader.fragmentShader + .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) + .replace( 'uniform float metalness;', 'uniform float glossiness;' ) + .replace( '#include ', specularMapParsFragmentChunk ) + .replace( '#include ', glossinessMapParsFragmentChunk ) + .replace( '#include ', specularMapFragmentChunk ) + .replace( '#include ', glossinessMapFragmentChunk ) + .replace( '#include ', lightPhysicalFragmentChunk ); - case "Waveform": - case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": - return "Waveform"; + }; - case "WetPaint": - case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": - case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": - return "WetPaint"; + Object.defineProperties( this, { - case "WigglyGraphite": - case "5347acf0-a8e2-47b6-8346-30c70719d763": - case "e814fef1-97fd-7194-4a2f-50c2bb918be2": - return "WigglyGraphite"; + specular: { + get: function () { + + return uniforms.specular.value; + + }, + set: function ( v ) { + + uniforms.specular.value = v; + + } + }, + + specularMap: { + get: function () { + + return uniforms.specularMap.value; + + }, + set: function ( v ) { + + uniforms.specularMap.value = v; + + if ( v ) { + + this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps + + } else { + + delete this.defines.USE_SPECULARMAP; + + } + + } + }, + + glossiness: { + get: function () { + + return uniforms.glossiness.value; + + }, + set: function ( v ) { + + uniforms.glossiness.value = v; + + } + }, + + glossinessMap: { + get: function () { + + return uniforms.glossinessMap.value; + + }, + set: function ( v ) { + + uniforms.glossinessMap.value = v; + + if ( v ) { + + this.defines.USE_GLOSSINESSMAP = ''; + this.defines.USE_UV = ''; + + } else { + + delete this.defines.USE_GLOSSINESSMAP; + delete this.defines.USE_UV; + + } + + } + } + + } ); + + delete this.metalness; + delete this.roughness; + delete this.metalnessMap; + delete this.roughnessMap; + + this.setValues( params ); + + } + + copy( source ) { + + super.copy( source ); + + this.specularMap = source.specularMap; + this.specular.copy( source.specular ); + this.glossinessMap = source.glossinessMap; + this.glossiness = source.glossiness; + delete this.metalness; + delete this.roughness; + delete this.metalnessMap; + delete this.roughnessMap; + return this; + + } - case "wire": - case "4391385a-cf83-4396-9e33-31e4e4930b27": - return "Wire"; - } } } - const tiltBrushMaterialParams = { - "BlocksBasic" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_Shininess: { value: 0.2 }, - u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 } - }, - vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", - fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, - depthTest: true, - blending: 0 - }, - "BlocksGem" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, - u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, - u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, - u_Color: { value: new Vector4(1, 1, 1, 1) }, - u_Shininess: { value: 0.9 }, - u_RimIntensity: { value: 0.5 }, - u_RimPower: { value: 2 }, - u_Frequency: { value: 2 }, - u_Jitter: { value: 1 }, - u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, - u_fogDensity: {value: 0 } - }, - vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", - fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, + + class GLTFMaterialsPbrSpecularGlossinessExtension { + + constructor() { + + this.name = EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; + + this.specularGlossinessParams = [ + 'color', + 'map', + 'lightMap', + 'lightMapIntensity', + 'aoMap', + 'aoMapIntensity', + 'emissive', + 'emissiveIntensity', + 'emissiveMap', + 'bumpMap', + 'bumpScale', + 'normalMap', + 'normalMapType', + 'displacementMap', + 'displacementScale', + 'displacementBias', + 'specularMap', + 'specular', + 'glossinessMap', + 'glossiness', + 'alphaMap', + 'envMap', + 'envMapIntensity' + ]; + + } + + getMaterialType() { + + return GLTFMeshStandardSGMaterial; + + } + + extendParams( materialParams, materialDef, parser ) { + + const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; + + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + const pending = []; + + if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { + + const array = pbrSpecularGlossiness.diffuseFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture, sRGBEncoding ) ); + + } + + materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); + materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; + materialParams.specular = new Color( 1.0, 1.0, 1.0 ); + + if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { + + materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); + + } + + if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { + + const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; + pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); + pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef, sRGBEncoding ) ); + + } + + return Promise.all( pending ); + + } + + createMaterial( materialParams ) { + + const material = new GLTFMeshStandardSGMaterial( materialParams ); + material.fog = true; + + material.color = materialParams.color; + + material.map = materialParams.map === undefined ? null : materialParams.map; + + material.lightMap = null; + material.lightMapIntensity = 1.0; + + material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; + material.aoMapIntensity = 1.0; + + material.emissive = materialParams.emissive; + material.emissiveIntensity = 1.0; + material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; + + material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; + material.bumpScale = 1; + + material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; + material.normalMapType = TangentSpaceNormalMap; + + if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; + + material.displacementMap = null; + material.displacementScale = 1; + material.displacementBias = 0; + + material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; + material.specular = materialParams.specular; + + material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; + material.glossiness = materialParams.glossiness; + + material.alphaMap = null; + + material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; + material.envMapIntensity = 1.0; + + return material; + + } + + } + + /** + * Mesh Quantization Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization + */ + class GLTFMeshQuantizationExtension { + + constructor() { + + this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + + } + + } + + /*********************************/ + /********** INTERPOLATION ********/ + /*********************************/ + + // Spline Interpolation + // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation + class GLTFCubicSplineInterpolant extends Interpolant { + + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + + } + + copySampleValue_( index ) { + + // Copies a sample value to the result buffer. See description of glTF + // CUBICSPLINE values layout in interpolate_() function below. + + const result = this.resultBuffer, + values = this.sampleValues, + valueSize = this.valueSize, + offset = index * valueSize * 3 + valueSize; + + for ( let i = 0; i !== valueSize; i ++ ) { + + result[ i ] = values[ offset + i ]; + + } + + return result; + + } + + } + + GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + + GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + + GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { + + const result = this.resultBuffer; + const values = this.sampleValues; + const stride = this.valueSize; + + const stride2 = stride * 2; + const stride3 = stride * 3; + + const td = t1 - t0; + + const p = ( t - t0 ) / td; + const pp = p * p; + const ppp = pp * p; + + const offset1 = i1 * stride3; + const offset0 = offset1 - stride3; + + const s2 = - 2 * ppp + 3 * pp; + const s3 = ppp - pp; + const s0 = 1 - s2; + const s1 = s3 - pp + p; + + // Layout of keyframe output values for CUBICSPLINE animations: + // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] + for ( let i = 0; i !== stride; i ++ ) { + + const p0 = values[ offset0 + i + stride ]; // splineVertex_k + const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) + const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 + const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) + + result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; + + } + + return result; + + }; + + const _q = new Quaternion(); + + class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { + + interpolate_( i1, t0, t, t1 ) { + + const result = super.interpolate_( i1, t0, t, t1 ); + + _q.fromArray( result ).normalize().toArray( result ); + + return result; + + } + + } + + + /*********************************/ + /********** INTERNALS ************/ + /*********************************/ + + /* CONSTANTS */ + + const WEBGL_CONSTANTS$1 = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + POINTS: 0, + LINES: 1, + LINE_LOOP: 2, + LINE_STRIP: 3, + TRIANGLES: 4, + TRIANGLE_STRIP: 5, + TRIANGLE_FAN: 6, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123 + }; + + const WEBGL_COMPONENT_TYPES$1 = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array + }; + + const WEBGL_FILTERS$1 = { + 9728: NearestFilter, + 9729: LinearFilter, + 9984: NearestMipmapNearestFilter, + 9985: LinearMipmapNearestFilter, + 9986: NearestMipmapLinearFilter, + 9987: LinearMipmapLinearFilter + }; + + const WEBGL_WRAPPINGS$1 = { + 33071: ClampToEdgeWrapping, + 33648: MirroredRepeatWrapping, + 10497: RepeatWrapping + }; + + const WEBGL_TYPE_SIZES$1 = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 + }; + + const ATTRIBUTES = { + POSITION: 'position', + NORMAL: 'normal', + TANGENT: 'tangent', + TEXCOORD_0: 'uv', + TEXCOORD_1: 'uv2', + COLOR_0: 'color', + WEIGHTS_0: 'skinWeight', + JOINTS_0: 'skinIndex', + }; + + const PATH_PROPERTIES$1 = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion', + weights: 'morphTargetInfluences' + }; + + const INTERPOLATION$1 = { + CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each + // keyframe track will be initialized with a default interpolation type, then modified. + LINEAR: InterpolateLinear, + STEP: InterpolateDiscrete + }; + + const ALPHA_MODES = { + OPAQUE: 'OPAQUE', + MASK: 'MASK', + BLEND: 'BLEND' + }; + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material + */ + function createDefaultMaterial$1( cache ) { + + if ( cache[ 'DefaultMaterial' ] === undefined ) { + + cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { + color: 0xFFFFFF, + emissive: 0x000000, + metalness: 1, + roughness: 1, + transparent: false, + depthTest: true, + side: FrontSide + } ); + + } + + return cache[ 'DefaultMaterial' ]; + + } + + function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { + + // Add unknown glTF extensions to an object's userData. + + for ( const name in objectDef.extensions ) { + + if ( knownExtensions[ name ] === undefined ) { + + object.userData.gltfExtensions = object.userData.gltfExtensions || {}; + object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; + + } + + } + + } + + /** + * @param {Object3D|Material|BufferGeometry} object + * @param {GLTF.definition} gltfDef + */ + function assignExtrasToUserData( object, gltfDef ) { + + if ( gltfDef.extras !== undefined ) { + + if ( typeof gltfDef.extras === 'object' ) { + + Object.assign( object.userData, gltfDef.extras ); + + } else { + + console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); + + } + + } + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets + * + * @param {BufferGeometry} geometry + * @param {Array} targets + * @param {GLTFParser} parser + * @return {Promise} + */ + function addMorphTargets( geometry, targets, parser ) { + + let hasMorphPosition = false; + let hasMorphNormal = false; + let hasMorphColor = false; + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( target.POSITION !== undefined ) hasMorphPosition = true; + if ( target.NORMAL !== undefined ) hasMorphNormal = true; + if ( target.COLOR_0 !== undefined ) hasMorphColor = true; + + if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break; + + } + + if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry ); + + const pendingPositionAccessors = []; + const pendingNormalAccessors = []; + const pendingColorAccessors = []; + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( hasMorphPosition ) { + + const pendingAccessor = target.POSITION !== undefined + ? parser.getDependency( 'accessor', target.POSITION ) + : geometry.attributes.position; + + pendingPositionAccessors.push( pendingAccessor ); + + } + + if ( hasMorphNormal ) { + + const pendingAccessor = target.NORMAL !== undefined + ? parser.getDependency( 'accessor', target.NORMAL ) + : geometry.attributes.normal; + + pendingNormalAccessors.push( pendingAccessor ); + + } + + if ( hasMorphColor ) { + + const pendingAccessor = target.COLOR_0 !== undefined + ? parser.getDependency( 'accessor', target.COLOR_0 ) + : geometry.attributes.color; + + pendingColorAccessors.push( pendingAccessor ); + + } + + } + + return Promise.all( [ + Promise.all( pendingPositionAccessors ), + Promise.all( pendingNormalAccessors ), + Promise.all( pendingColorAccessors ) + ] ).then( function ( accessors ) { + + const morphPositions = accessors[ 0 ]; + const morphNormals = accessors[ 1 ]; + const morphColors = accessors[ 2 ]; + + if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; + if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; + if ( hasMorphColor ) geometry.morphAttributes.color = morphColors; + geometry.morphTargetsRelative = true; + + return geometry; + + } ); + + } + + /** + * @param {Mesh} mesh + * @param {GLTF.Mesh} meshDef + */ + function updateMorphTargets( mesh, meshDef ) { + + mesh.updateMorphTargets(); + + if ( meshDef.weights !== undefined ) { + + for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { + + mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; + + } + + } + + // .extras has user-defined data, so check that .extras.targetNames is an array. + if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { + + const targetNames = meshDef.extras.targetNames; + + if ( mesh.morphTargetInfluences.length === targetNames.length ) { + + mesh.morphTargetDictionary = {}; + + for ( let i = 0, il = targetNames.length; i < il; i ++ ) { + + mesh.morphTargetDictionary[ targetNames[ i ] ] = i; + + } + + } else { + + console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); + + } + + } + + } + + function createPrimitiveKey( primitiveDef ) { + + const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; + let geometryKey; + + if ( dracoExtension ) { + + geometryKey = 'draco:' + dracoExtension.bufferView + + ':' + dracoExtension.indices + + ':' + createAttributesKey( dracoExtension.attributes ); + + } else { + + geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; + + } + + return geometryKey; + + } + + function createAttributesKey( attributes ) { + + let attributesKey = ''; + + const keys = Object.keys( attributes ).sort(); + + for ( let i = 0, il = keys.length; i < il; i ++ ) { + + attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; + + } + + return attributesKey; + + } + + function getNormalizedComponentScale( constructor ) { + + // Reference: + // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data + + switch ( constructor ) { + + case Int8Array: + return 1 / 127; + + case Uint8Array: + return 1 / 255; + + case Int16Array: + return 1 / 32767; + + case Uint16Array: + return 1 / 65535; + + default: + throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); + + } + + } + + function getImageURIMimeType( uri ) { + + if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; + if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; + + return 'image/png'; + + } + + /* GLTF PARSER */ + + class GLTFParser$1 { + + constructor( json = {}, options = {} ) { + + this.json = json; + this.extensions = {}; + this.plugins = {}; + this.options = options; + + // loader object cache + this.cache = new GLTFRegistry$1(); + + // associations between Three.js objects and glTF elements + this.associations = new Map(); + + // BufferGeometry caching + this.primitiveCache = {}; + + // Object3D instance caches + this.meshCache = { refs: {}, uses: {} }; + this.cameraCache = { refs: {}, uses: {} }; + this.lightCache = { refs: {}, uses: {} }; + + this.sourceCache = {}; + this.textureCache = {}; + + // Track node names, to ensure no duplicates + this.nodeNamesUsed = {}; + + // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the + // expensive work of uploading a texture to the GPU off the main thread. + if ( typeof createImageBitmap !== 'undefined' && /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === false ) { + + this.textureLoader = new ImageBitmapLoader( this.options.manager ); + + } else { + + this.textureLoader = new TextureLoader( this.options.manager ); + + } + + this.textureLoader.setCrossOrigin( this.options.crossOrigin ); + this.textureLoader.setRequestHeader( this.options.requestHeader ); + + this.fileLoader = new FileLoader( this.options.manager ); + this.fileLoader.setResponseType( 'arraybuffer' ); + + if ( this.options.crossOrigin === 'use-credentials' ) { + + this.fileLoader.setWithCredentials( true ); + + } + + } + + setExtensions( extensions ) { + + this.extensions = extensions; + + } + + setPlugins( plugins ) { + + this.plugins = plugins; + + } + + parse( onLoad, onError ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + + // Clear the loader cache + this.cache.removeAll(); + + // Mark the special nodes/meshes in json for efficient parse + this._invokeAll( function ( ext ) { + + return ext._markDefs && ext._markDefs(); + + } ); + + Promise.all( this._invokeAll( function ( ext ) { + + return ext.beforeRoot && ext.beforeRoot(); + + } ) ).then( function () { + + return Promise.all( [ + + parser.getDependencies( 'scene' ), + parser.getDependencies( 'animation' ), + parser.getDependencies( 'camera' ), + + ] ); + + } ).then( function ( dependencies ) { + + const result = { + scene: dependencies[ 0 ][ json.scene || 0 ], + scenes: dependencies[ 0 ], + animations: dependencies[ 1 ], + cameras: dependencies[ 2 ], + asset: json.asset, + parser: parser, + userData: {} + }; + + addUnknownExtensionsToUserData( extensions, result, json ); + + assignExtrasToUserData( result, json ); + + Promise.all( parser._invokeAll( function ( ext ) { + + return ext.afterRoot && ext.afterRoot( result ); + + } ) ).then( function () { + + onLoad( result ); + + } ); + + } ).catch( onError ); + + } + + /** + * Marks the special nodes/meshes in json for efficient parse. + */ + _markDefs() { + + const nodeDefs = this.json.nodes || []; + const skinDefs = this.json.skins || []; + const meshDefs = this.json.meshes || []; + + // Nothing in the node definition indicates whether it is a Bone or an + // Object3D. Use the skins' joint references to mark bones. + for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { + + const joints = skinDefs[ skinIndex ].joints; + + for ( let i = 0, il = joints.length; i < il; i ++ ) { + + nodeDefs[ joints[ i ] ].isBone = true; + + } + + } + + // Iterate over all nodes, marking references to shared resources, + // as well as skeleton joints. + for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + + const nodeDef = nodeDefs[ nodeIndex ]; + + if ( nodeDef.mesh !== undefined ) { + + this._addNodeRef( this.meshCache, nodeDef.mesh ); + + // Nothing in the mesh definition indicates whether it is + // a SkinnedMesh or Mesh. Use the node's mesh reference + // to mark SkinnedMesh if node has skin. + if ( nodeDef.skin !== undefined ) { + + meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; + + } + + } + + if ( nodeDef.camera !== undefined ) { + + this._addNodeRef( this.cameraCache, nodeDef.camera ); + + } + + } + + } + + /** + * Counts references to shared node / Object3D resources. These resources + * can be reused, or "instantiated", at multiple nodes in the scene + * hierarchy. Mesh, Camera, and Light instances are instantiated and must + * be marked. Non-scenegraph resources (like Materials, Geometries, and + * Textures) can be reused directly and are not marked here. + * + * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. + */ + _addNodeRef( cache, index ) { + + if ( index === undefined ) return; + + if ( cache.refs[ index ] === undefined ) { + + cache.refs[ index ] = cache.uses[ index ] = 0; + + } + + cache.refs[ index ] ++; + + } + + /** Returns a reference to a shared resource, cloning it if necessary. */ + _getNodeRef( cache, index, object ) { + + if ( cache.refs[ index ] <= 1 ) return object; + + const ref = object.clone(); + + // Propagates mappings to the cloned object, prevents mappings on the + // original object from being lost. + const updateMappings = ( original, clone ) => { + + const mappings = this.associations.get( original ); + if ( mappings != null ) { + + this.associations.set( clone, mappings ); + + } + + for ( const [ i, child ] of original.children.entries() ) { + + updateMappings( child, clone.children[ i ] ); + + } + + }; + + updateMappings( object, ref ); + + ref.name += '_instance_' + ( cache.uses[ index ] ++ ); + + return ref; + + } + + _invokeOne( func ) { + + const extensions = Object.values( this.plugins ); + extensions.push( this ); + + for ( let i = 0; i < extensions.length; i ++ ) { + + const result = func( extensions[ i ] ); + + if ( result ) return result; + + } + + return null; + + } + + _invokeAll( func ) { + + const extensions = Object.values( this.plugins ); + extensions.unshift( this ); + + const pending = []; + + for ( let i = 0; i < extensions.length; i ++ ) { + + const result = func( extensions[ i ] ); + + if ( result ) pending.push( result ); + + } + + return pending; + + } + + /** + * Requests the specified dependency asynchronously, with caching. + * @param {string} type + * @param {number} index + * @return {Promise} + */ + getDependency( type, index ) { + + const cacheKey = type + ':' + index; + let dependency = this.cache.get( cacheKey ); + + if ( ! dependency ) { + + switch ( type ) { + + case 'scene': + dependency = this.loadScene( index ); + break; + + case 'node': + dependency = this.loadNode( index ); + break; + + case 'mesh': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadMesh && ext.loadMesh( index ); + + } ); + break; + + case 'accessor': + dependency = this.loadAccessor( index ); + break; + + case 'bufferView': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadBufferView && ext.loadBufferView( index ); + + } ); + break; + + case 'buffer': + dependency = this.loadBuffer( index ); + break; + + case 'material': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadMaterial && ext.loadMaterial( index ); + + } ); + break; + + case 'texture': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadTexture && ext.loadTexture( index ); + + } ); + break; + + case 'skin': + dependency = this.loadSkin( index ); + break; + + case 'animation': + dependency = this.loadAnimation( index ); + break; + + case 'camera': + dependency = this.loadCamera( index ); + break; + + default: + throw new Error( 'Unknown type: ' + type ); + + } + + this.cache.add( cacheKey, dependency ); + + } + + return dependency; + + } + + /** + * Requests all dependencies of the specified type asynchronously, with caching. + * @param {string} type + * @return {Promise>} + */ + getDependencies( type ) { + + let dependencies = this.cache.get( type ); + + if ( ! dependencies ) { + + const parser = this; + const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; + + dependencies = Promise.all( defs.map( function ( def, index ) { + + return parser.getDependency( type, index ); + + } ) ); + + this.cache.add( type, dependencies ); + + } + + return dependencies; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferIndex + * @return {Promise} + */ + loadBuffer( bufferIndex ) { + + const bufferDef = this.json.buffers[ bufferIndex ]; + const loader = this.fileLoader; + + if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { + + throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); + + } + + // If present, GLB container is required to be the first buffer. + if ( bufferDef.uri === undefined && bufferIndex === 0 ) { + + return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + + } + + const options = this.options; + + return new Promise( function ( resolve, reject ) { + + loader.load( LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { + + reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); + + } ); + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferViewIndex + * @return {Promise} + */ + loadBufferView( bufferViewIndex ) { + + const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; + + return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { + + const byteLength = bufferViewDef.byteLength || 0; + const byteOffset = bufferViewDef.byteOffset || 0; + return buffer.slice( byteOffset, byteOffset + byteLength ); + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors + * @param {number} accessorIndex + * @return {Promise} + */ + loadAccessor( accessorIndex ) { + + const parser = this; + const json = this.json; + + const accessorDef = this.json.accessors[ accessorIndex ]; + + if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { + + // Ignore empty accessors, which may be used to declare runtime + // information about attributes coming from another source (e.g. Draco + // compression extension). + return Promise.resolve( null ); + + } + + const pendingBufferViews = []; + + if ( accessorDef.bufferView !== undefined ) { + + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); + + } else { + + pendingBufferViews.push( null ); + + } + + if ( accessorDef.sparse !== undefined ) { + + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); + + } + + return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { + + const bufferView = bufferViews[ 0 ]; + + const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + const elementBytes = TypedArray.BYTES_PER_ELEMENT; + const itemBytes = elementBytes * itemSize; + const byteOffset = accessorDef.byteOffset || 0; + const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; + const normalized = accessorDef.normalized === true; + let array, bufferAttribute; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( byteStride && byteStride !== itemBytes ) { + + // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer + // This makes sure that IBA.count reflects accessor.count properly + const ibSlice = Math.floor( byteOffset / byteStride ); + const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; + let ib = parser.cache.get( ibCacheKey ); + + if ( ! ib ) { + + array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + ib = new InterleavedBuffer( array, byteStride / elementBytes ); + + parser.cache.add( ibCacheKey, ib ); + + } + + bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); + + } else { + + if ( bufferView === null ) { + + array = new TypedArray( accessorDef.count * itemSize ); + + } else { + + array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); + + } + + bufferAttribute = new BufferAttribute( array, itemSize, normalized ); + + } + + // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors + if ( accessorDef.sparse !== undefined ) { + + const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; + const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + + const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; + const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; + + const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); + const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); + + if ( bufferView !== null ) { + + // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. + bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); + + } + + for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { + + const index = sparseIndices[ i ]; + + bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); + if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); + if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); + if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); + if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); + + } + + } + + return bufferAttribute; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures + * @param {number} textureIndex + * @return {Promise} + */ + loadTexture( textureIndex ) { + + const json = this.json; + const options = this.options; + const textureDef = json.textures[ textureIndex ]; + const sourceIndex = textureDef.source; + const sourceDef = json.images[ sourceIndex ]; + + let loader = this.textureLoader; + + if ( sourceDef.uri ) { + + const handler = options.manager.getHandler( sourceDef.uri ); + if ( handler !== null ) loader = handler; + + } + + return this.loadTextureImage( textureIndex, sourceIndex, loader ); + + } + + loadTextureImage( textureIndex, sourceIndex, loader ) { + + const parser = this; + const json = this.json; + + const textureDef = json.textures[ textureIndex ]; + const sourceDef = json.images[ sourceIndex ]; + + const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler; + + if ( this.textureCache[ cacheKey ] ) { + + // See https://github.com/mrdoob/three.js/issues/21559. + return this.textureCache[ cacheKey ]; + + } + + const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) { + + texture.flipY = false; + + if ( textureDef.name ) texture.name = textureDef.name; + + const samplers = json.samplers || {}; + const sampler = samplers[ textureDef.sampler ] || {}; + + texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || LinearFilter; + texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || LinearMipmapLinearFilter; + texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || RepeatWrapping; + texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || RepeatWrapping; + + parser.associations.set( texture, { textures: textureIndex } ); + + return texture; + + } ).catch( function () { + + return null; + + } ); + + this.textureCache[ cacheKey ] = promise; + + return promise; + + } + + loadImageSource( sourceIndex, loader ) { + + const parser = this; + const json = this.json; + const options = this.options; + + if ( this.sourceCache[ sourceIndex ] !== undefined ) { + + return this.sourceCache[ sourceIndex ].then( ( texture ) => texture.clone() ); + + } + + const sourceDef = json.images[ sourceIndex ]; + + const URL = self.URL || self.webkitURL; + + let sourceURI = sourceDef.uri || ''; + let isObjectURL = false; + + if ( sourceDef.bufferView !== undefined ) { + + // Load binary image data from bufferView, if provided. + + sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) { + + isObjectURL = true; + const blob = new Blob( [ bufferView ], { type: sourceDef.mimeType } ); + sourceURI = URL.createObjectURL( blob ); + return sourceURI; + + } ); + + } else if ( sourceDef.uri === undefined ) { + + throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' ); + + } + + const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { + + return new Promise( function ( resolve, reject ) { + + let onLoad = resolve; + + if ( loader.isImageBitmapLoader === true ) { + + onLoad = function ( imageBitmap ) { + + const texture = new Texture( imageBitmap ); + texture.needsUpdate = true; + + resolve( texture ); + + }; + + } + + loader.load( LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); + + } ); + + } ).then( function ( texture ) { + + // Clean up resources and configure Texture. + + if ( isObjectURL === true ) { + + URL.revokeObjectURL( sourceURI ); + + } + + texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); + + return texture; + + } ).catch( function ( error ) { + + console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI ); + throw error; + + } ); + + this.sourceCache[ sourceIndex ] = promise; + return promise; + + } + + /** + * Asynchronously assigns a texture to the given material parameters. + * @param {Object} materialParams + * @param {string} mapName + * @param {Object} mapDef + * @return {Promise} + */ + assignTexture( materialParams, mapName, mapDef, encoding ) { + + const parser = this; + + return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { + + // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured + // However, we will copy UV set 0 to UV set 1 on demand for aoMap + if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { + + console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); + + } + + if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + + const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + + if ( transform ) { + + const gltfReference = parser.associations.get( texture ); + texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); + parser.associations.set( texture, gltfReference ); + + } + + } + + if ( encoding !== undefined ) { + + texture.encoding = encoding; + + } + + materialParams[ mapName ] = texture; + + return texture; + + } ); + + } + + /** + * Assigns final material to a Mesh, Line, or Points instance. The instance + * already has a material (generated from the glTF material options alone) + * but reuse of the same glTF material may require multiple threejs materials + * to accommodate different primitive types, defines, etc. New materials will + * be created if necessary, and reused from a cache. + * @param {Object3D} mesh Mesh, Line, or Points instance. + */ + assignFinalMaterial( mesh ) { + + const geometry = mesh.geometry; + let material = mesh.material; + + const useDerivativeTangents = geometry.attributes.tangent === undefined; + const useVertexColors = geometry.attributes.color !== undefined; + const useFlatShading = geometry.attributes.normal === undefined; + + if ( mesh.isPoints ) { + + const cacheKey = 'PointsMaterial:' + material.uuid; + + let pointsMaterial = this.cache.get( cacheKey ); + + if ( ! pointsMaterial ) { + + pointsMaterial = new PointsMaterial(); + Material.prototype.copy.call( pointsMaterial, material ); + pointsMaterial.color.copy( material.color ); + pointsMaterial.map = material.map; + pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px + + this.cache.add( cacheKey, pointsMaterial ); + + } + + material = pointsMaterial; + + } else if ( mesh.isLine ) { + + const cacheKey = 'LineBasicMaterial:' + material.uuid; + + let lineMaterial = this.cache.get( cacheKey ); + + if ( ! lineMaterial ) { + + lineMaterial = new LineBasicMaterial(); + Material.prototype.copy.call( lineMaterial, material ); + lineMaterial.color.copy( material.color ); + + this.cache.add( cacheKey, lineMaterial ); + + } + + material = lineMaterial; + + } + + // Clone the material if it will be modified + if ( useDerivativeTangents || useVertexColors || useFlatShading ) { + + let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; + + if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; + if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; + if ( useVertexColors ) cacheKey += 'vertex-colors:'; + if ( useFlatShading ) cacheKey += 'flat-shading:'; + + let cachedMaterial = this.cache.get( cacheKey ); + + if ( ! cachedMaterial ) { + + cachedMaterial = material.clone(); + + if ( useVertexColors ) cachedMaterial.vertexColors = true; + if ( useFlatShading ) cachedMaterial.flatShading = true; + + if ( useDerivativeTangents ) { + + // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 + if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; + if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; + + } + + this.cache.add( cacheKey, cachedMaterial ); + + this.associations.set( cachedMaterial, this.associations.get( material ) ); + + } + + material = cachedMaterial; + + } + + // workarounds for mesh and geometry + + if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { + + geometry.setAttribute( 'uv2', geometry.attributes.uv ); + + } + + mesh.material = material; + + } + + getMaterialType( /* materialIndex */ ) { + + return MeshStandardMaterial; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials + * @param {number} materialIndex + * @return {Promise} + */ + loadMaterial( materialIndex ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + const materialDef = json.materials[ materialIndex ]; + + let materialType; + const materialParams = {}; + const materialExtensions = materialDef.extensions || {}; + + const pending = []; + + if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { + + const sgExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; + materialType = sgExtension.getMaterialType(); + pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); + + } else if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + + const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; + materialType = kmuExtension.getMaterialType(); + pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); + + } else { + + // Specification: + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material + + const metallicRoughness = materialDef.pbrMetallicRoughness || {}; + + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + const array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) ); + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); + pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); + + } + + materialType = this._invokeOne( function ( ext ) { + + return ext.getMaterialType && ext.getMaterialType( materialIndex ); + + } ); + + pending.push( Promise.all( this._invokeAll( function ( ext ) { + + return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); + + } ) ) ); + + } + + if ( materialDef.doubleSided === true ) { + + materialParams.side = DoubleSide; + + } + + const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; + + if ( alphaMode === ALPHA_MODES.BLEND ) { + + materialParams.transparent = true; + + // See: https://github.com/mrdoob/three.js/issues/17706 + materialParams.depthWrite = false; + + } else { + + materialParams.transparent = false; + + if ( alphaMode === ALPHA_MODES.MASK ) { + + materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; + + } + + } + + if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); + + materialParams.normalScale = new Vector2( 1, 1 ); + + if ( materialDef.normalTexture.scale !== undefined ) { + + const scale = materialDef.normalTexture.scale; + + materialParams.normalScale.set( scale, scale ); + + } + + } + + if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); + + if ( materialDef.occlusionTexture.strength !== undefined ) { + + materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; + + } + + } + + if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { + + materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); + + } + + if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, sRGBEncoding ) ); + + } + + return Promise.all( pending ).then( function () { + + let material; + + if ( materialType === GLTFMeshStandardSGMaterial ) { + + material = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); + + } else { + + material = new materialType( materialParams ); + + } + + if ( materialDef.name ) material.name = materialDef.name; + + assignExtrasToUserData( material, materialDef ); + + parser.associations.set( material, { materials: materialIndex } ); + + if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); + + return material; + + } ); + + } + + /** When Object3D instances are targeted by animation, they need unique names. */ + createUniqueName( originalName ) { + + const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); + + let name = sanitizedName; + + for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { + + name = sanitizedName + '_' + i; + + } + + this.nodeNamesUsed[ name ] = true; + + return name; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry + * + * Creates BufferGeometries from primitives. + * + * @param {Array} primitives + * @return {Promise>} + */ + loadGeometries( primitives ) { + + const parser = this; + const extensions = this.extensions; + const cache = this.primitiveCache; + + function createDracoPrimitive( primitive ) { + + return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] + .decodePrimitive( primitive, parser ) + .then( function ( geometry ) { + + return addPrimitiveAttributes( geometry, primitive, parser ); + + } ); + + } + + const pending = []; + + for ( let i = 0, il = primitives.length; i < il; i ++ ) { + + const primitive = primitives[ i ]; + const cacheKey = createPrimitiveKey( primitive ); + + // See if we've already created this geometry + const cached = cache[ cacheKey ]; + + if ( cached ) { + + // Use the cached geometry if it exists + pending.push( cached.promise ); + + } else { + + let geometryPromise; + + if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + + // Use DRACO geometry if available + geometryPromise = createDracoPrimitive( primitive ); + + } else { + + // Otherwise create a new geometry + geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); + + } + + // Cache this geometry + cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; + + pending.push( geometryPromise ); + + } + + } + + return Promise.all( pending ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes + * @param {number} meshIndex + * @return {Promise} + */ + loadMesh( meshIndex ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + + const meshDef = json.meshes[ meshIndex ]; + const primitives = meshDef.primitives; + + const pending = []; + + for ( let i = 0, il = primitives.length; i < il; i ++ ) { + + const material = primitives[ i ].material === undefined + ? createDefaultMaterial$1( this.cache ) + : this.getDependency( 'material', primitives[ i ].material ); + + pending.push( material ); + + } + + pending.push( parser.loadGeometries( primitives ) ); + + return Promise.all( pending ).then( function ( results ) { + + const materials = results.slice( 0, results.length - 1 ); + const geometries = results[ results.length - 1 ]; + + const meshes = []; + + for ( let i = 0, il = geometries.length; i < il; i ++ ) { + + const geometry = geometries[ i ]; + const primitive = primitives[ i ]; + + // 1. create Mesh + + let mesh; + + const material = materials[ i ]; + + if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || + primitive.mode === undefined ) { + + // .isSkinnedMesh isn't in glTF spec. See ._markDefs() + mesh = meshDef.isSkinnedMesh === true + ? new SkinnedMesh( geometry, material ) + : new Mesh( geometry, material ); + + if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { + + // we normalize floating point skin weight array to fix malformed assets (see #15319) + // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs + mesh.normalizeSkinWeights(); + + } + + if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); + + } + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + + mesh = new LineSegments( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + + mesh = new Line( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + + mesh = new LineLoop( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + + mesh = new Points( geometry, material ); + + } else { + + throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); + + } + + if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { + + updateMorphTargets( mesh, meshDef ); + + } + + mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); + + assignExtrasToUserData( mesh, meshDef ); + + if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); + + parser.assignFinalMaterial( mesh ); + + meshes.push( mesh ); + + } + + for ( let i = 0, il = meshes.length; i < il; i ++ ) { + + parser.associations.set( meshes[ i ], { + meshes: meshIndex, + primitives: i + } ); + + } + + if ( meshes.length === 1 ) { + + return meshes[ 0 ]; + + } + + const group = new Group(); + + parser.associations.set( group, { meshes: meshIndex } ); + + for ( let i = 0, il = meshes.length; i < il; i ++ ) { + + group.add( meshes[ i ] ); + + } + + return group; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras + * @param {number} cameraIndex + * @return {Promise} + */ + loadCamera( cameraIndex ) { + + let camera; + const cameraDef = this.json.cameras[ cameraIndex ]; + const params = cameraDef[ cameraDef.type ]; + + if ( ! params ) { + + console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); + return; + + } + + if ( cameraDef.type === 'perspective' ) { + + camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); + + } else if ( cameraDef.type === 'orthographic' ) { + + camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); + + } + + if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); + + assignExtrasToUserData( camera, cameraDef ); + + return Promise.resolve( camera ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins + * @param {number} skinIndex + * @return {Promise} + */ + loadSkin( skinIndex ) { + + const skinDef = this.json.skins[ skinIndex ]; + + const skinEntry = { joints: skinDef.joints }; + + if ( skinDef.inverseBindMatrices === undefined ) { + + return Promise.resolve( skinEntry ); + + } + + return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { + + skinEntry.inverseBindMatrices = accessor; + + return skinEntry; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations + * @param {number} animationIndex + * @return {Promise} + */ + loadAnimation( animationIndex ) { + + const json = this.json; + + const animationDef = json.animations[ animationIndex ]; + + const pendingNodes = []; + const pendingInputAccessors = []; + const pendingOutputAccessors = []; + const pendingSamplers = []; + const pendingTargets = []; + + for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { + + const channel = animationDef.channels[ i ]; + const sampler = animationDef.samplers[ channel.sampler ]; + const target = channel.target; + const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. + const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; + const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; + + pendingNodes.push( this.getDependency( 'node', name ) ); + pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); + pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); + pendingSamplers.push( sampler ); + pendingTargets.push( target ); + + } + + return Promise.all( [ + + Promise.all( pendingNodes ), + Promise.all( pendingInputAccessors ), + Promise.all( pendingOutputAccessors ), + Promise.all( pendingSamplers ), + Promise.all( pendingTargets ) + + ] ).then( function ( dependencies ) { + + const nodes = dependencies[ 0 ]; + const inputAccessors = dependencies[ 1 ]; + const outputAccessors = dependencies[ 2 ]; + const samplers = dependencies[ 3 ]; + const targets = dependencies[ 4 ]; + + const tracks = []; + + for ( let i = 0, il = nodes.length; i < il; i ++ ) { + + const node = nodes[ i ]; + const inputAccessor = inputAccessors[ i ]; + const outputAccessor = outputAccessors[ i ]; + const sampler = samplers[ i ]; + const target = targets[ i ]; + + if ( node === undefined ) continue; + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + let TypedKeyframeTrack; + + switch ( PATH_PROPERTIES$1[ target.path ] ) { + + case PATH_PROPERTIES$1.weights: + + TypedKeyframeTrack = NumberKeyframeTrack; + break; + + case PATH_PROPERTIES$1.rotation: + + TypedKeyframeTrack = QuaternionKeyframeTrack; + break; + + case PATH_PROPERTIES$1.position: + case PATH_PROPERTIES$1.scale: + default: + + TypedKeyframeTrack = VectorKeyframeTrack; + break; + + } + + const targetName = node.name ? node.name : node.uuid; + + const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : InterpolateLinear; + + const targetNames = []; + + if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + + node.traverse( function ( object ) { + + if ( object.morphTargetInfluences ) { + + targetNames.push( object.name ? object.name : object.uuid ); + + } + + } ); + + } else { + + targetNames.push( targetName ); + + } + + let outputArray = outputAccessor.array; + + if ( outputAccessor.normalized ) { + + const scale = getNormalizedComponentScale( outputArray.constructor ); + const scaled = new Float32Array( outputArray.length ); + + for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { + + scaled[ j ] = outputArray[ j ] * scale; + + } + + outputArray = scaled; + + } + + for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { + + const track = new TypedKeyframeTrack( + targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], + inputAccessor.array, + outputArray, + interpolation + ); + + // Override interpolation with custom factory method. + if ( sampler.interpolation === 'CUBICSPLINE' ) { + + track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { + + // A CUBICSPLINE keyframe in glTF has three output values for each input value, + // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() + // must be divided by three to get the interpolant's sampleSize argument. + + const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; + + return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); + + }; + + // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. + track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; + + } + + tracks.push( track ); + + } + + } + + const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; + + return new AnimationClip( name, undefined, tracks ); + + } ); + + } + + createNodeMesh( nodeIndex ) { + + const json = this.json; + const parser = this; + const nodeDef = json.nodes[ nodeIndex ]; + + if ( nodeDef.mesh === undefined ) return null; + + return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { + + const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); + + // if weights are provided on the node, override weights on the mesh. + if ( nodeDef.weights !== undefined ) { + + node.traverse( function ( o ) { + + if ( ! o.isMesh ) return; + + for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { + + o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; + + } + + } ); + + } + + return node; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy + * @param {number} nodeIndex + * @return {Promise} + */ + loadNode( nodeIndex ) { + + const json = this.json; + const extensions = this.extensions; + const parser = this; + + const nodeDef = json.nodes[ nodeIndex ]; + + // reserve node's name before its dependencies, so the root has the intended name. + const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; + + return ( function () { + + const pending = []; + + const meshPromise = parser._invokeOne( function ( ext ) { + + return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); + + } ); + + if ( meshPromise ) { + + pending.push( meshPromise ); + + } + + if ( nodeDef.camera !== undefined ) { + + pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { + + return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); + + } ) ); + + } + + parser._invokeAll( function ( ext ) { + + return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); + + } ).forEach( function ( promise ) { + + pending.push( promise ); + + } ); + + return Promise.all( pending ); + + }() ).then( function ( objects ) { + + let node; + + // .isBone isn't in glTF spec. See ._markDefs + if ( nodeDef.isBone === true ) { + + node = new Bone(); + + } else if ( objects.length > 1 ) { + + node = new Group(); + + } else if ( objects.length === 1 ) { + + node = objects[ 0 ]; + + } else { + + node = new Object3D(); + + } + + if ( node !== objects[ 0 ] ) { + + for ( let i = 0, il = objects.length; i < il; i ++ ) { + + node.add( objects[ i ] ); + + } + + } + + if ( nodeDef.name ) { + + node.userData.name = nodeDef.name; + node.name = nodeName; + + } + + assignExtrasToUserData( node, nodeDef ); + + if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); + + if ( nodeDef.matrix !== undefined ) { + + const matrix = new Matrix4(); + matrix.fromArray( nodeDef.matrix ); + node.applyMatrix4( matrix ); + + } else { + + if ( nodeDef.translation !== undefined ) { + + node.position.fromArray( nodeDef.translation ); + + } + + if ( nodeDef.rotation !== undefined ) { + + node.quaternion.fromArray( nodeDef.rotation ); + + } + + if ( nodeDef.scale !== undefined ) { + + node.scale.fromArray( nodeDef.scale ); + + } + + } + + if ( ! parser.associations.has( node ) ) { + + parser.associations.set( node, {} ); + + } + + parser.associations.get( node ).nodes = nodeIndex; + + return node; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes + * @param {number} sceneIndex + * @return {Promise} + */ + loadScene( sceneIndex ) { + + const json = this.json; + const extensions = this.extensions; + const sceneDef = this.json.scenes[ sceneIndex ]; + const parser = this; + + // Loader returns Group, not Scene. + // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 + const scene = new Group(); + if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); + + assignExtrasToUserData( scene, sceneDef ); + + if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); + + const nodeIds = sceneDef.nodes || []; + + const pending = []; + + for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { + + pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) ); + + } + + return Promise.all( pending ).then( function () { + + // Removes dangling associations, associations that reference a node that + // didn't make it into the scene. + const reduceAssociations = ( node ) => { + + const reducedAssociations = new Map(); + + for ( const [ key, value ] of parser.associations ) { + + if ( key instanceof Material || key instanceof Texture ) { + + reducedAssociations.set( key, value ); + + } + + } + + node.traverse( ( node ) => { + + const mappings = parser.associations.get( node ); + + if ( mappings != null ) { + + reducedAssociations.set( node, mappings ); + + } + + } ); + + return reducedAssociations; + + }; + + parser.associations = reduceAssociations( scene ); + + return scene; + + } ); + + } + + } + + function buildNodeHierarchy( nodeId, parentObject, json, parser ) { + + const nodeDef = json.nodes[ nodeId ]; + + return parser.getDependency( 'node', nodeId ).then( function ( node ) { + + if ( nodeDef.skin === undefined ) return node; + + // build skeleton here as well + + let skinEntry; + + return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { + + skinEntry = skin; + + const pendingJoints = []; + + for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { + + pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); + + } + + return Promise.all( pendingJoints ); + + } ).then( function ( jointNodes ) { + + node.traverse( function ( mesh ) { + + if ( ! mesh.isMesh ) return; + + const bones = []; + const boneInverses = []; + + for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { + + const jointNode = jointNodes[ j ]; + + if ( jointNode ) { + + bones.push( jointNode ); + + const mat = new Matrix4(); + + if ( skinEntry.inverseBindMatrices !== undefined ) { + + mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); + + } + + boneInverses.push( mat ); + + } else { + + console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); + + } + + } + + mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); + + } ); + + return node; + + } ); + + } ).then( function ( node ) { + + // build node hierachy + + parentObject.add( node ); + + const pending = []; + + if ( nodeDef.children ) { + + const children = nodeDef.children; + + for ( let i = 0, il = children.length; i < il; i ++ ) { + + const child = children[ i ]; + pending.push( buildNodeHierarchy( child, node, json, parser ) ); + + } + + } + + return Promise.all( pending ); + + } ); + + } + + /** + * @param {BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + */ + function computeBounds( geometry, primitiveDef, parser ) { + + const attributes = primitiveDef.attributes; + + const box = new Box3(); + + if ( attributes.POSITION !== undefined ) { + + const accessor = parser.json.accessors[ attributes.POSITION ]; + + const min = accessor.min; + const max = accessor.max; + + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + + if ( min !== undefined && max !== undefined ) { + + box.set( + new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), + new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) + ); + + if ( accessor.normalized ) { + + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + box.min.multiplyScalar( boxScale ); + box.max.multiplyScalar( boxScale ); + + } + + } else { + + console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + + return; + + } + + } else { + + return; + + } + + const targets = primitiveDef.targets; + + if ( targets !== undefined ) { + + const maxDisplacement = new Vector3(); + const vector = new Vector3(); + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( target.POSITION !== undefined ) { + + const accessor = parser.json.accessors[ target.POSITION ]; + const min = accessor.min; + const max = accessor.max; + + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + + if ( min !== undefined && max !== undefined ) { + + // we need to get max of absolute components because target weight is [-1,1] + vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); + vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); + vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); + + + if ( accessor.normalized ) { + + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + vector.multiplyScalar( boxScale ); + + } + + // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative + // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets + // are used to implement key-frame animations and as such only two are active at a time - this results in very large + // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. + maxDisplacement.max( vector ); + + } else { + + console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + + } + + } + + } + + // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. + box.expandByVector( maxDisplacement ); + + } + + geometry.boundingBox = box; + + const sphere = new Sphere(); + + box.getCenter( sphere.center ); + sphere.radius = box.min.distanceTo( box.max ) / 2; + + geometry.boundingSphere = sphere; + + } + + /** + * @param {BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + * @return {Promise} + */ + function addPrimitiveAttributes( geometry, primitiveDef, parser ) { + + const attributes = primitiveDef.attributes; + + const pending = []; + + function assignAttributeAccessor( accessorIndex, attributeName ) { + + return parser.getDependency( 'accessor', accessorIndex ) + .then( function ( accessor ) { + + geometry.setAttribute( attributeName, accessor ); + + } ); + + } + + for ( const gltfAttributeName in attributes ) { + + const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + + // Skip attributes already provided by e.g. Draco extension. + if ( threeAttributeName in geometry.attributes ) continue; + + pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); + + } + + if ( primitiveDef.indices !== undefined && ! geometry.index ) { + + const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { + + geometry.setIndex( accessor ); + + } ); + + pending.push( accessor ); + + } + + assignExtrasToUserData( geometry, primitiveDef ); + + computeBounds( geometry, primitiveDef, parser ); + + return Promise.all( pending ).then( function () { + + return primitiveDef.targets !== undefined + ? addMorphTargets( geometry, primitiveDef.targets, parser ) + : geometry; + + } ); + + } + + /** + * @param {BufferGeometry} geometry + * @param {Number} drawMode + * @return {BufferGeometry} + */ + function toTrianglesDrawMode( geometry, drawMode ) { + + let index = geometry.getIndex(); + + // generate index if not present + + if ( index === null ) { + + const indices = []; + + const position = geometry.getAttribute( 'position' ); + + if ( position !== undefined ) { + + for ( let i = 0; i < position.count; i ++ ) { + + indices.push( i ); + + } + + geometry.setIndex( indices ); + index = geometry.getIndex(); + + } else { + + console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); + return geometry; + + } + + } + + // + + const numberOfTriangles = index.count - 2; + const newIndices = []; + + if ( drawMode === TriangleFanDrawMode ) { + + // gl.TRIANGLE_FAN + + for ( let i = 1; i <= numberOfTriangles; i ++ ) { + + newIndices.push( index.getX( 0 ) ); + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + + } + + } else { + + // gl.TRIANGLE_STRIP + + for ( let i = 0; i < numberOfTriangles; i ++ ) { + + if ( i % 2 === 0 ) { + + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i + 2 ) ); + + + } else { + + newIndices.push( index.getX( i + 2 ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i ) ); + + } + + } + + } + + if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { + + console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); + + } + + // build final geometry + + const newGeometry = geometry.clone(); + newGeometry.setIndex( newIndices ); + + return newGeometry; + + } + + class VRButton { + + static createButton( renderer, options ) { + + if ( options ) { + + console.error( 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.' ); + + } + + const button = document.createElement( 'button' ); + + function showEnterVR( /*device*/ ) { + + let currentSession = null; + + async function onSessionStarted( session ) { + + session.addEventListener( 'end', onSessionEnded ); + + await renderer.xr.setSession( session ); + button.textContent = 'EXIT VR'; + + currentSession = session; + + } + + function onSessionEnded( /*event*/ ) { + + currentSession.removeEventListener( 'end', onSessionEnded ); + + button.textContent = 'ENTER VR'; + + currentSession = null; + + } + + // + + button.style.display = ''; + + button.style.cursor = 'pointer'; + button.style.left = 'calc(50% - 50px)'; + button.style.width = '100px'; + + button.textContent = 'ENTER VR'; + + button.onmouseenter = function () { + + button.style.opacity = '1.0'; + + }; + + button.onmouseleave = function () { + + button.style.opacity = '0.5'; + + }; + + button.onclick = function () { + + if ( currentSession === null ) { + + // WebXR's requestReferenceSpace only works if the corresponding feature + // was requested at session creation time. For simplicity, just ask for + // the interesting ones as optional features, but be aware that the + // requestReferenceSpace call will fail if it turns out to be unavailable. + // ('local' is always available for immersive sessions and doesn't need to + // be requested separately.) + + const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking', 'layers' ] }; + navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted ); + + } else { + + currentSession.end(); + + } + + }; + + } + + function disableButton() { + + button.style.display = ''; + + button.style.cursor = 'auto'; + button.style.left = 'calc(50% - 75px)'; + button.style.width = '150px'; + + button.onmouseenter = null; + button.onmouseleave = null; + + button.onclick = null; + + } + + function showWebXRNotFound() { + + disableButton(); + + button.textContent = 'VR NOT SUPPORTED'; + + } + + function showVRNotAllowed( exception ) { + + disableButton(); + + console.warn( 'Exception when trying to call xr.isSessionSupported', exception ); + + button.textContent = 'VR NOT ALLOWED'; + + } + + function stylizeElement( element ) { + + element.style.position = 'absolute'; + element.style.bottom = '20px'; + element.style.padding = '12px 6px'; + element.style.border = '1px solid #fff'; + element.style.borderRadius = '4px'; + element.style.background = 'rgba(0,0,0,0.1)'; + element.style.color = '#fff'; + element.style.font = 'normal 13px sans-serif'; + element.style.textAlign = 'center'; + element.style.opacity = '0.5'; + element.style.outline = 'none'; + element.style.zIndex = '999'; + + } + + if ( 'xr' in navigator ) { + + button.id = 'VRButton'; + button.style.display = 'none'; + + stylizeElement( button ); + + navigator.xr.isSessionSupported( 'immersive-vr' ).then( function ( supported ) { + + supported ? showEnterVR() : showWebXRNotFound(); + + if ( supported && VRButton.xrSessionIsGranted ) { + + button.click(); + + } + + } ).catch( showVRNotAllowed ); + + return button; + + } else { + + const message = document.createElement( 'a' ); + + if ( window.isSecureContext === false ) { + + message.href = document.location.href.replace( /^http:/, 'https:' ); + message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message + + } else { + + message.href = 'https://immersiveweb.dev/'; + message.innerHTML = 'WEBXR NOT AVAILABLE'; + + } + + message.style.left = 'calc(50% - 90px)'; + message.style.width = '180px'; + message.style.textDecoration = 'none'; + + stylizeElement( message ); + + return message; + + } + + } + + static xrSessionIsGranted = false; + + static registerSessionGrantedListener() { + + if ( 'xr' in navigator ) { + + navigator.xr.addEventListener( 'sessiongranted', () => { + + VRButton.xrSessionIsGranted = true; + + } ); + + } + + } + + } + + VRButton.registerSessionGrantedListener(); + + /*! + * three-icosa + * https://github.com/icosa-gallery/three-icosa + * Copyright (c) 2021-2022 Icosa Gallery + * Released under the Apache 2.0 Licence. + */ + + // Copyright 2021-2022 Icosa Gallery + + class TiltShaderLoader$1 extends Loader { + constructor( manager ) { + super( manager ); + + this.loadedMaterials = {}; + } + + async load(brushName, onLoad, onProgress, onError ) { + const scope = this; + + const isAlreadyLoaded = this.loadedMaterials[brushName]; + + if (isAlreadyLoaded !== undefined) { + onLoad( scope.parse( isAlreadyLoaded ) ); + return; + } + + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + + const textureLoader = new TextureLoader(this.manager); + textureLoader.setPath(this.path); + textureLoader.setWithCredentials( this.withCredentials ); + + const materialParams = tiltBrushMaterialParams$1[brushName]; + + materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); + materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + + if (materialParams.uniforms.u_MainTex) { + const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); + mainTex.name = `${brushName}_MainTex`; + mainTex.wrapS = RepeatWrapping; + mainTex.wrapT = RepeatWrapping; + mainTex.flipY = false; + materialParams.uniforms.u_MainTex.value = mainTex; + } + + if (materialParams.uniforms.u_BumpMap) { + const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); + bumpMap.name = `${brushName}_BumpMap`; + bumpMap.wrapS = RepeatWrapping; + bumpMap.wrapT = RepeatWrapping; + bumpMap.flipY = false; + materialParams.uniforms.u_BumpMap.value = bumpMap; + } + + if (materialParams.uniforms.u_AlphaMask) { + const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); + alphaMask.name = `${brushName}_AlphaMask`; + alphaMask.wrapS = RepeatWrapping; + alphaMask.wrapT = RepeatWrapping; + alphaMask.flipY = false; + materialParams.uniforms.u_AlphaMask.value = alphaMask; + } + + // inject three.js lighting uniforms + for(var lightType in UniformsLib.lights) + { + materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; + } + + let rawMaterial = new RawShaderMaterial(materialParams); + this.loadedMaterials[brushName] = rawMaterial; + onLoad( scope.parse( rawMaterial ) ); + } + + parse( rawMaterial ) { + return rawMaterial; + } + + lookupMaterial(nameOrGuid) { + const name = this.lookupMaterialName(nameOrGuid); + return tiltBrushMaterialParams$1[name]; + } + + lookupMaterialName(nameOrGuid) { + switch(nameOrGuid) { + case "BlocksBasic:": + case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": + return "BlocksBasic"; + + case "BlocksGem": + case "232998f8-d357-47a2-993a-53415df9be10": + return "BlocksGem"; + + case "BlocksGlass": + case "3d813d82-5839-4450-8ddc-8e889ecd96c7": + return "BlocksGlass"; + + case "Bubbles": + case "89d104cd-d012-426b-b5b3-bbaee63ac43c": + return "Bubbles"; + + case "CelVinyl": + case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": + return "CelVinyl"; + + case "ChromaticWave": + case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": + return "ChromaticWave"; + + case "CoarseBristles": + case "1161af82-50cf-47db-9706-0c3576d43c43": + case "79168f10-6961-464a-8be1-57ed364c5600": + return "CoarseBristles"; + + case "Comet": + case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": + return "Comet"; + + case "DiamondHull": + case "c8313697-2563-47fc-832e-290f4c04b901": + return "DiamondHull"; + + case "Disco": + case "4391aaaa-df73-4396-9e33-31e4e4930b27": + return "Disco"; + + case "DotMarker": + case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": + return "DotMarker"; + + case "Dots": + case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": + return "Dots"; + + case "DoubleTaperedFlat": + case "0d3889f3-3ede-470c-8af4-f44813306126": + return "DoubleTaperedFlat"; + + case "DoubleTaperedMarker": + case "0d3889f3-3ede-470c-8af4-de4813306126": + return "DoubleTaperedMarker"; + + case "DuctTape": + case "d0262945-853c-4481-9cbd-88586bed93cb": + case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": + return "DuctTape"; + + case "Electricity": + case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": + return "Electricity"; + + case "Embers": + case "02ffb866-7fb2-4d15-b761-1012cefb1360": + return "Embers"; + + case "EnvironmentDiffuse": + case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": + return "EnvironmentDiffuse"; + + case "EnvironmentDiffuseLightMap": + case "d01d9d6c-9a61-4aba-8146-5891fafb013b": + return "EnvironmentDiffuseLightMap"; + + case "Fire": + case "cb92b597-94ca-4255-b017-0e3f42f12f9e": + return "Fire"; + + case "2d35bcf0-e4d8-452c-97b1-3311be063130": + case "280c0a7a-aad8-416c-a7d2-df63d129ca70": + case "55303bc4-c749-4a72-98d9-d23e68e76e18": + case "Flat": + return "Flat"; + + case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": + case "geometry_Highlighter": + return "Highlighter"; + + case "Hypercolor": + case "dce872c2-7b49-4684-b59b-c45387949c5c": + case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": + return "Hypercolor"; + + case "HyperGrid": + case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": + return "HyperGrid"; + + case "Icing": + case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": + return "Icing"; + + case "Ink": + case "f5c336cf-5108-4b40-ade9-c687504385ab": + case "c0012095-3ffd-4040-8ee1-fc180d346eaa": + return "Ink"; + + case "Leaves": + case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": + case "ea19de07-d0c0-4484-9198-18489a3c1487": + return "Leaves"; + + case "Light": + case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": + return "Light"; + + case "LightWire": + case "4391aaaa-df81-4396-9e33-31e4e4930b27": + return "LightWire"; + + case "Lofted": + case "d381e0f5-3def-4a0d-8853-31e9200bcbda": + return "Lofted"; + + case "Marker": + case "429ed64a-4e97-4466-84d3-145a861ef684": + return "Marker"; + + case "MatteHull": + case "79348357-432d-4746-8e29-0e25c112e3aa": + return "MatteHull"; + + case "NeonPulse": + case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": + return "NeonPulse"; + + case "OilPaint": + case "f72ec0e7-a844-4e38-82e3-140c44772699": + case "c515dad7-4393-4681-81ad-162ef052241b": + return "OilPaint"; + + case "Paper": + case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": + case "759f1ebd-20cd-4720-8d41-234e0da63716": + return "Paper"; + + case "PbrTemplate": + case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": + return "PbrTemplate"; + + case "PbrTransparentTemplate": + case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": + return "PbrTransparentTemplate"; + + case "Petal": + case "e0abbc80-0f80-e854-4970-8924a0863dcc": + return "Petal"; + + case "Plasma": + case "c33714d1-b2f9-412e-bd50-1884c9d46336": + return "Plasma"; + + case "Rainbow": + case "ad1ad437-76e2-450d-a23a-e17f8310b960": + return "Rainbow"; + + case "ShinyHull": + case "faaa4d44-fcfb-4177-96be-753ac0421ba3": + return "ShinyHull"; + + case "Smoke": + case "70d79cca-b159-4f35-990c-f02193947fe8": + return "Smoke"; + + case "Snow": + case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": + return "Snow"; + + case "SoftHighlighter": + case "accb32f5-4509-454f-93f8-1df3fd31df1b": + return "SoftHighlighter"; + + case "Spikes": + case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": + return "Spikes"; + + case "Splatter": + case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": + case "7a1c8107-50c5-4b70-9a39-421576d6617e": + return "Splatter"; + + case "Stars": + case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": + return "Stars"; + + case "Streamers": + case "44bb800a-fbc3-4592-8426-94ecb05ddec3": + return "Streamers"; + + case "Taffy": + case "0077f88c-d93a-42f3-b59b-b31c50cdb414": + return "Taffy"; + + case "TaperedFlat": + case "b468c1fb-f254-41ed-8ec9-57030bc5660c": + case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": + return "TaperedFlat"; + + case "TaperedMarker": + case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": + case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": + return "TaperedMarker"; + + case "ThickPaint": + case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": + case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": + return "ThickPaint"; + + case "Toon": + case "4391385a-df73-4396-9e33-31e4e4930b27": + return "Toon"; + + case "UnlitHull": + case "a8fea537-da7c-4d4b-817f-24f074725d6d": + return "UnlitHull"; + + case "VelvetInk": + case "d229d335-c334-495a-a801-660ac8a87360": + return "VelvetInk"; + + case "Waveform": + case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": + return "Waveform"; + + case "WetPaint": + case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": + case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": + return "WetPaint"; + + case "WigglyGraphite": + case "5347acf0-a8e2-47b6-8346-30c70719d763": + case "e814fef1-97fd-7194-4a2f-50c2bb918be2": + return "WigglyGraphite"; + + case "wire": + case "4391385a-cf83-4396-9e33-31e4e4930b27": + return "Wire"; + } } + } + + const tiltBrushMaterialParams$1 = { + "BlocksBasic" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.2 }, + u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", + fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGem" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, + u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, + u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.9 }, + u_RimIntensity: { value: 0.5 }, + u_RimPower: { value: 2 }, + u_Frequency: { value: 2 }, + u_Jitter: { value: 1 }, + u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, + u_fogDensity: {value: 0 } + }, + vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", + fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, depthTest: true, blending: 0 }, @@ -48552,7 +51563,7 @@ this.brushPath += "/"; } - this.tiltShaderLoader = new TiltShaderLoader(parser.options.manager); + this.tiltShaderLoader = new TiltShaderLoader$1(parser.options.manager); this.tiltShaderLoader.setPath(brushPath); this.clock = new Clock(); } @@ -48603,26 +51614,25 @@ scene.traverse(async object => { const association = parser.associations.get(object); - if (association === undefined || association.type !== "nodes") { - return; - } - - const node = json.nodes[association.index]; - if (node.mesh === undefined) { + if (association === undefined || association.meshes === undefined) { return; } - const prim = json.meshes[node.mesh].primitives[0]; - const mat = json.materials[prim.material]; - const extensionsDef = mat.extensions; - - if (!extensionsDef || !extensionsDef[this.name]) { - return; - } - - const guid = extensionsDef.GOOGLE_tilt_brush_material.guid; + const mesh = json.meshes[association.meshes]; + mesh.primitives.forEach((prim) => { + if(!prim.material) { + return; + } - shaderResolves.push(this.replaceMaterial(object, guid)); + const mat = json.materials[prim.material]; + if (!mat.extensions || !mat.extensions[this.name]) { + return; + } + + const guid = mat.extensions.GOOGLE_tilt_brush_material.guid; + + shaderResolves.push(this.replaceMaterial(object, guid)); + }); }); } @@ -48631,8 +51641,6 @@ async replaceMaterial(mesh, guid) { let shader; - let updateTime = false; - let updateCamera = false; switch(guid) { case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": @@ -48716,7 +51724,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_ChromaticWave"; - updateTime = true; break; case "1161af82-50cf-47db-9706-0c3576d43c43": @@ -48746,7 +51753,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Comet"; - updateTime = true; break; case "c8313697-2563-47fc-832e-290f4c04b901": @@ -48761,8 +51767,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_DiamondHull"; - updateTime = true; - updateCamera = true; break; case "4391aaaa-df73-4396-9e33-31e4e4930b27": @@ -48777,7 +51781,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Disco"; - updateTime = true; break; case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": @@ -48863,7 +51866,6 @@ shader = await this.tiltShaderLoader.loadAsync("Electricity"); mesh.material = shader; mesh.material.name = "material_Electricity"; - updateTime = true; break; case "02ffb866-7fb2-4d15-b761-1012cefb1360": @@ -48877,7 +51879,6 @@ shader = await this.tiltShaderLoader.loadAsync("Embers"); mesh.material = shader; mesh.material.name = "material_Embers"; - updateTime = true; break; case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": @@ -48920,7 +51921,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Fire"; - updateTime = true; break; case "2d35bcf0-e4d8-452c-97b1-3311be063130": @@ -48966,7 +51966,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Hypercolor"; - updateTime = true; break; case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": @@ -49055,7 +52054,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_LightWire"; - updateTime = true; break; case "d381e0f5-3def-4a0d-8853-31e9200bcbda": @@ -49111,7 +52109,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_NeonPulse"; - updateTime = true; break; case "f72ec0e7-a844-4e38-82e3-140c44772699": @@ -49198,7 +52195,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Plasma"; - updateTime = true; break; case "ad1ad437-76e2-450d-a23a-e17f8310b960": @@ -49213,7 +52209,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Rainbow"; - updateTime = true; break; case "faaa4d44-fcfb-4177-96be-753ac0421ba3": @@ -49255,7 +52250,6 @@ shader = await this.tiltShaderLoader.loadAsync("Snow"); mesh.material = shader; mesh.material.name = "material_Snow"; - updateTime = true; break; case "accb32f5-4509-454f-93f8-1df3fd31df1b": @@ -49314,7 +52308,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Stars"; - updateTime = true; break; case "44bb800a-fbc3-4592-8426-94ecb05ddec3": @@ -49329,7 +52322,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Streamers"; - updateTime = true; break; case "0077f88c-d93a-42f3-b59b-b31c50cdb414": @@ -49344,7 +52336,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Taffy"; - updateTime = true; break; case "b468c1fb-f254-41ed-8ec9-57030bc5660c": @@ -49442,7 +52433,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Waveform"; - updateTime = true; break; case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": @@ -49473,7 +52463,6 @@ shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_WigglyGraphite"; - updateTime = true; break; case "4391385a-cf83-4396-9e33-31e4e4930b27": @@ -49491,17 +52480,16 @@ } mesh.onBeforeRender = (renderer, scene, camera, geometry, material, group) => { - // Check for any dynamic uniforms set. - if(updateTime || updateCamera) { + if (material.uniforms["u_time"]) { const elapsedTime = this.clock.getElapsedTime(); // _Time from https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html const time = new Vector4(elapsedTime/20, elapsedTime, elapsedTime*2, elapsedTime*3); - if (updateTime) { - material.uniforms["u_time"].value = time; - } - if (updateCamera) { - material.uniforms["cameraPosition"].value = camera.position; - } + + material.uniforms["u_time"].value = time; + } + + if (material.uniforms["cameraPosition"]) { + material.uniforms["cameraPosition"].value = camera.position; } if(material?.uniforms?.directionalLights?.value) { @@ -49936,57 +52924,1644 @@ return out; } } - // skip local zip header - var slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); }; - // read zip header - var zh = function (d, b, z) { - var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20); - var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2]; - return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off]; - }; - // read zip64 extra field - var z64e = function (d, b) { - for (; b2(d, b) != 1; b += 4 + b2(d, b + 2)) - ; - return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)]; + // skip local zip header + var slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); }; + // read zip header + var zh = function (d, b, z) { + var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20); + var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2]; + return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off]; + }; + // read zip64 extra field + var z64e = function (d, b) { + for (; b2(d, b) != 1; b += 4 + b2(d, b + 2)) + ; + return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)]; + }; + /** + * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better + * performance with more than one file. + * @param data The raw compressed ZIP file + * @returns The decompressed files + */ + function unzipSync(data) { + var files = {}; + var e = data.length - 22; + for (; b4(data, e) != 0x6054B50; --e) { + if (!e || data.length - e > 65558) + throw 'invalid zip file'; + } + var c = b2(data, e + 8); + if (!c) + return {}; + var o = b4(data, e + 16); + var z = o == 4294967295; + if (z) { + e = b4(data, e - 12); + if (b4(data, e) != 0x6064B50) + throw 'invalid zip file'; + c = b4(data, e + 32); + o = b4(data, e + 48); + } + for (var i = 0; i < c; ++i) { + var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off); + o = no; + if (!c_2) + files[fn] = slc(data, b, b + sc); + else if (c_2 == 8) + files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su)); + else + throw 'unknown compression type ' + c_2; + } + return files; + } + + /*! + * three-icosa + * https://github.com/icosa-gallery/three-icosa + * Copyright (c) 2021-2022 Icosa Gallery + * Released under the Apache 2.0 Licence. + */ + + // Copyright 2021-2022 Icosa Gallery + + class TiltShaderLoader extends Loader { + constructor( manager ) { + super( manager ); + + this.loadedMaterials = {}; + } + + async load(brushName, onLoad, onProgress, onError ) { + const scope = this; + + const isAlreadyLoaded = this.loadedMaterials[brushName]; + + if (isAlreadyLoaded !== undefined) { + onLoad( scope.parse( isAlreadyLoaded ) ); + return; + } + + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + + const textureLoader = new TextureLoader(this.manager); + textureLoader.setPath(this.path); + textureLoader.setWithCredentials( this.withCredentials ); + + const materialParams = tiltBrushMaterialParams[brushName]; + + materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); + materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + + if (materialParams.uniforms.u_MainTex) { + const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); + mainTex.name = `${brushName}_MainTex`; + mainTex.wrapS = RepeatWrapping; + mainTex.wrapT = RepeatWrapping; + mainTex.flipY = false; + materialParams.uniforms.u_MainTex.value = mainTex; + } + + if (materialParams.uniforms.u_BumpMap) { + const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); + bumpMap.name = `${brushName}_BumpMap`; + bumpMap.wrapS = RepeatWrapping; + bumpMap.wrapT = RepeatWrapping; + bumpMap.flipY = false; + materialParams.uniforms.u_BumpMap.value = bumpMap; + } + + if (materialParams.uniforms.u_AlphaMask) { + const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); + alphaMask.name = `${brushName}_AlphaMask`; + alphaMask.wrapS = RepeatWrapping; + alphaMask.wrapT = RepeatWrapping; + alphaMask.flipY = false; + materialParams.uniforms.u_AlphaMask.value = alphaMask; + } + + // inject three.js lighting uniforms + for(var lightType in UniformsLib.lights) + { + materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; + } + + let rawMaterial = new RawShaderMaterial(materialParams); + this.loadedMaterials[brushName] = rawMaterial; + onLoad( scope.parse( rawMaterial ) ); + } + + parse( rawMaterial ) { + return rawMaterial; + } + + lookupMaterial(nameOrGuid) { + const name = this.lookupMaterialName(nameOrGuid); + return tiltBrushMaterialParams[name]; + } + + lookupMaterialName(nameOrGuid) { + switch(nameOrGuid) { + case "BlocksBasic:": + case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": + return "BlocksBasic"; + + case "BlocksGem": + case "232998f8-d357-47a2-993a-53415df9be10": + return "BlocksGem"; + + case "BlocksGlass": + case "3d813d82-5839-4450-8ddc-8e889ecd96c7": + return "BlocksGlass"; + + case "Bubbles": + case "89d104cd-d012-426b-b5b3-bbaee63ac43c": + return "Bubbles"; + + case "CelVinyl": + case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": + return "CelVinyl"; + + case "ChromaticWave": + case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": + return "ChromaticWave"; + + case "CoarseBristles": + case "1161af82-50cf-47db-9706-0c3576d43c43": + case "79168f10-6961-464a-8be1-57ed364c5600": + return "CoarseBristles"; + + case "Comet": + case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": + return "Comet"; + + case "DiamondHull": + case "c8313697-2563-47fc-832e-290f4c04b901": + return "DiamondHull"; + + case "Disco": + case "4391aaaa-df73-4396-9e33-31e4e4930b27": + return "Disco"; + + case "DotMarker": + case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": + return "DotMarker"; + + case "Dots": + case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": + return "Dots"; + + case "DoubleTaperedFlat": + case "0d3889f3-3ede-470c-8af4-f44813306126": + return "DoubleTaperedFlat"; + + case "DoubleTaperedMarker": + case "0d3889f3-3ede-470c-8af4-de4813306126": + return "DoubleTaperedMarker"; + + case "DuctTape": + case "d0262945-853c-4481-9cbd-88586bed93cb": + case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": + return "DuctTape"; + + case "Electricity": + case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": + return "Electricity"; + + case "Embers": + case "02ffb866-7fb2-4d15-b761-1012cefb1360": + return "Embers"; + + case "EnvironmentDiffuse": + case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": + return "EnvironmentDiffuse"; + + case "EnvironmentDiffuseLightMap": + case "d01d9d6c-9a61-4aba-8146-5891fafb013b": + return "EnvironmentDiffuseLightMap"; + + case "Fire": + case "cb92b597-94ca-4255-b017-0e3f42f12f9e": + return "Fire"; + + case "2d35bcf0-e4d8-452c-97b1-3311be063130": + case "280c0a7a-aad8-416c-a7d2-df63d129ca70": + case "55303bc4-c749-4a72-98d9-d23e68e76e18": + case "Flat": + return "Flat"; + + case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": + case "geometry_Highlighter": + return "Highlighter"; + + case "Hypercolor": + case "dce872c2-7b49-4684-b59b-c45387949c5c": + case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": + return "Hypercolor"; + + case "HyperGrid": + case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": + return "HyperGrid"; + + case "Icing": + case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": + return "Icing"; + + case "Ink": + case "f5c336cf-5108-4b40-ade9-c687504385ab": + case "c0012095-3ffd-4040-8ee1-fc180d346eaa": + return "Ink"; + + case "Leaves": + case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": + case "ea19de07-d0c0-4484-9198-18489a3c1487": + return "Leaves"; + + case "Light": + case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": + return "Light"; + + case "LightWire": + case "4391aaaa-df81-4396-9e33-31e4e4930b27": + return "LightWire"; + + case "Lofted": + case "d381e0f5-3def-4a0d-8853-31e9200bcbda": + return "Lofted"; + + case "Marker": + case "429ed64a-4e97-4466-84d3-145a861ef684": + return "Marker"; + + case "MatteHull": + case "79348357-432d-4746-8e29-0e25c112e3aa": + return "MatteHull"; + + case "NeonPulse": + case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": + return "NeonPulse"; + + case "OilPaint": + case "f72ec0e7-a844-4e38-82e3-140c44772699": + case "c515dad7-4393-4681-81ad-162ef052241b": + return "OilPaint"; + + case "Paper": + case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": + case "759f1ebd-20cd-4720-8d41-234e0da63716": + return "Paper"; + + case "PbrTemplate": + case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": + return "PbrTemplate"; + + case "PbrTransparentTemplate": + case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": + return "PbrTransparentTemplate"; + + case "Petal": + case "e0abbc80-0f80-e854-4970-8924a0863dcc": + return "Petal"; + + case "Plasma": + case "c33714d1-b2f9-412e-bd50-1884c9d46336": + return "Plasma"; + + case "Rainbow": + case "ad1ad437-76e2-450d-a23a-e17f8310b960": + return "Rainbow"; + + case "ShinyHull": + case "faaa4d44-fcfb-4177-96be-753ac0421ba3": + return "ShinyHull"; + + case "Smoke": + case "70d79cca-b159-4f35-990c-f02193947fe8": + return "Smoke"; + + case "Snow": + case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": + return "Snow"; + + case "SoftHighlighter": + case "accb32f5-4509-454f-93f8-1df3fd31df1b": + return "SoftHighlighter"; + + case "Spikes": + case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": + return "Spikes"; + + case "Splatter": + case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": + case "7a1c8107-50c5-4b70-9a39-421576d6617e": + return "Splatter"; + + case "Stars": + case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": + return "Stars"; + + case "Streamers": + case "44bb800a-fbc3-4592-8426-94ecb05ddec3": + return "Streamers"; + + case "Taffy": + case "0077f88c-d93a-42f3-b59b-b31c50cdb414": + return "Taffy"; + + case "TaperedFlat": + case "b468c1fb-f254-41ed-8ec9-57030bc5660c": + case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": + return "TaperedFlat"; + + case "TaperedMarker": + case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": + case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": + return "TaperedMarker"; + + case "ThickPaint": + case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": + case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": + return "ThickPaint"; + + case "Toon": + case "4391385a-df73-4396-9e33-31e4e4930b27": + return "Toon"; + + case "UnlitHull": + case "a8fea537-da7c-4d4b-817f-24f074725d6d": + return "UnlitHull"; + + case "VelvetInk": + case "d229d335-c334-495a-a801-660ac8a87360": + return "VelvetInk"; + + case "Waveform": + case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": + return "Waveform"; + + case "WetPaint": + case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": + case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": + return "WetPaint"; + + case "WigglyGraphite": + case "5347acf0-a8e2-47b6-8346-30c70719d763": + case "e814fef1-97fd-7194-4a2f-50c2bb918be2": + return "WigglyGraphite"; + + case "wire": + case "4391385a-cf83-4396-9e33-31e4e4930b27": + return "Wire"; + } } + } + + const tiltBrushMaterialParams = { + "BlocksBasic" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.2 }, + u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", + fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGem" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, + u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, + u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.9 }, + u_RimIntensity: { value: 0.5 }, + u_RimPower: { value: 2 }, + u_Frequency: { value: 2 }, + u_Jitter: { value: 1 }, + u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, + u_fogDensity: {value: 0 } + }, + vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", + fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGlass" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.8 }, + u_RimIntensity: { value: 0.7 }, + u_RimPower: { value: 4 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl", + fragmentShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Bubbles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png" }, + }, + vertexShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl", + fragmentShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "CelVinyl" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_Cutoff: { value: 0.554 }, + u_MainTex: { value: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl", + fragmentShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "ChromaticWave" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.45 }, + }, + vertexShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl", + fragmentShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201 + }, + "CoarseBristles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.25 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl", + fragmentShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Comet" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png" }, + u_AlphaMask: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png" }, + u_AlphaMask_TexelSize: { value: new Vector4(0.0156, 1, 64, 1)}, + u_time: { value: new Vector4() }, + u_Speed: { value: 1 }, + u_EmissionGain: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl", + fragmentShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "DiamondHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + cameraPosition: { value: new Vector3() }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl", + fragmentShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Disco" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.65 }, + u_SpecColor: { value: new Vector3(0.5147059, 0.5147059, 0.5147059) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DotMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-vertex.glsl", + fragmentShader: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + + }, + "Dots" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-MainTex.png" }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_EmissionGain: { value: 300 }, + u_BaseGain: { value: 0.4 } + }, + vertexShader: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-vertex.glsl", + fragmentShader: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "DoubleTaperedFlat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.1500 }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-vertex.glsl", + fragmentShader: "DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DoubleTaperedMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-vertex.glsl", + fragmentShader: "DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DuctTape" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.5372549, 0.5372549, 0.5372549) }, + u_Shininess: { value: 0.414 }, + u_MainTex: { value: "DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-vertex.glsl", + fragmentShader: "DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Electricity" : { + uniforms: { + u_time: { value: new Vector4() }, + u_DisplacementIntensity: { value: 2.0 }, + u_EmissionGain: { value: 0.2 } + }, + vertexShader: "Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-vertex.glsl", + fragmentShader: "Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Embers" : { + uniforms: { + u_time: { value: new Vector4() }, + u_ScrollRate: { value: 0.6 }, + u_ScrollDistance: { value: new Vector3(-0.2, 0.6, 0) }, + u_ScrollJitterIntensity: { value: 0.03 }, + u_ScrollJitterFrequency: { value: 5 }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-vertex.glsl", + fragmentShader: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "EnvironmentDiffuse" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-vertex.glsl", + fragmentShader: "EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "EnvironmentDiffuseLightMap" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-vertex.glsl", + fragmentShader: "EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Fire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.5 } + }, + vertexShader: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-vertex.glsl", + fragmentShader: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201 + }, + "Flat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-vertex.glsl", + fragmentShader: "Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Highlighter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.12 } + }, + vertexShader: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-vertex.glsl", + fragmentShader: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Hypercolor" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.5 }, + u_SpecColor: { value: new Vector3(0.2745098, 0.2745098, 0.2745098) }, + u_MainTex: { value: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + u_Cutoff: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-vertex.glsl", + fragmentShader: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "HyperGrid" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-MainTex.png" } + }, + vertexShader: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-vertex.glsl", + fragmentShader: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Icing" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.1500 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-vertex.glsl", + fragmentShader: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Ink" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-vertex.glsl", + fragmentShader: "Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Leaves" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.395 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-vertex.glsl", + fragmentShader: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Light" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-MainTex.png" }, + u_EmissionGain: { value: 0.45 }, + }, + vertexShader: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-vertex.glsl", + fragmentShader: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "LightWire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.81 }, + u_SpecColor: { value: new Vector3(0.3455882, 0.3455882, 0.3455882) }, + u_time: { value: new Vector4() }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_MainTex: { value: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-MainTex.png"} + }, + vertexShader: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Lofted" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-vertex.glsl", + fragmentShader: "Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Marker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.067 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-vertex.glsl", + fragmentShader: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + + }, + "MatteHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-vertex.glsl", + fragmentShader: "MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "NeonPulse" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.5 }, + }, + vertexShader: "NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-vertex.glsl", + fragmentShader: "NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "OilPaint": { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0020, 0.0020, 512, 512) }, + }, + vertexShader: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-vertex.glsl", + fragmentShader: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Paper" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.145 }, + u_Cutoff: { value: 0.16 }, + u_MainTex: { value: "Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-vertex.glsl", + fragmentShader: "Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "PbrTemplate" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-vertex.glsl", + fragmentShader: "PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "PbrTransparentTemplate" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-vertex.glsl", + fragmentShader: "PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Petal" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.01 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-vertex.glsl", + fragmentShader: "Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + // How did an experimental brush end up here? + "Plasma" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-MainTex.png" }, + u_time: { value: new Vector4() } + }, + vertexShader: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-vertex.glsl", + fragmentShader: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Rainbow" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.65 } + }, + vertexShader: "Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-vertex.glsl", + fragmentShader: "Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "ShinyHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.1985294, 0.1985294, 0.1985294) }, + u_Shininess: { value: 0.7430 }, + u_Cutoff: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-vertex.glsl", + fragmentShader: "ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Smoke": { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-MainTex.png" } + }, + vertexShader: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-vertex.glsl", + fragmentShader: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Snow" : { + uniforms: { + u_time: { value: new Vector4() }, + u_ScrollRate: { value: 0.2 }, + u_ScrollDistance: { value: new Vector3(0, -0.3, 0) }, + u_ScrollJitterIntensity: { value: 0.01 }, + u_ScrollJitterFrequency: { value: 12 }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-MainTex.png" }, + }, + vertexShader: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-vertex.glsl", + fragmentShader: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "SoftHighlighter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-MainTex.png" }, + }, + vertexShader: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-vertex.glsl", + fragmentShader: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Spikes" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-vertex.glsl", + fragmentShader: "Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Splatter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-vertex.glsl", + fragmentShader: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Stars" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_SparkleRate: { value: 5.3 }, + u_MainTex: { value: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-MainTex.png" }, + }, + vertexShader: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-vertex.glsl", + fragmentShader: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Streamers" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-MainTex.png" }, + u_EmissionGain: { value: 0.4 }, + u_time: { value: new Vector4() }, + }, + vertexShader: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-vertex.glsl", + fragmentShader: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Taffy" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-vertex.glsl", + fragmentShader: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedFlat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.067 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-vertex.glsl", + fragmentShader: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-vertex.glsl", + fragmentShader: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedMarker_Flat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_MainTex: { value: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-vertex.glsl", + fragmentShader: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "ThickPaint" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-vertex.glsl", + fragmentShader: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Toon" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "UnlitHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-vertex.glsl", + fragmentShader: "UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "VelvetInk" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-vertex.glsl", + fragmentShader: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Waveform" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_EmissionGain: { value: 0.5178571 }, + u_time: { value: new Vector4() }, + u_MainTex: { value: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-MainTex.png" }, + }, + vertexShader: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-vertex.glsl", + fragmentShader: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "WetPaint" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.1397059, 0.1397059, 0.1397059) }, + u_Shininess: { value: 0.85 }, + u_Cutoff: { value: 0.3 }, + u_MainTex: { value: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-vertex.glsl", + fragmentShader: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "WigglyGraphite" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-vertex.glsl", + fragmentShader: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Wire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, }; - /** - * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better - * performance with more than one file. - * @param data The raw compressed ZIP file - * @returns The decompressed files - */ - function unzipSync(data) { - var files = {}; - var e = data.length - 22; - for (; b4(data, e) != 0x6054B50; --e) { - if (!e || data.length - e > 65558) - throw 'invalid zip file'; - } - var c = b2(data, e + 8); - if (!c) - return {}; - var o = b4(data, e + 16); - var z = o == 4294967295; - if (z) { - e = b4(data, e - 12); - if (b4(data, e) != 0x6064B50) - throw 'invalid zip file'; - c = b4(data, e + 32); - o = b4(data, e + 48); - } - for (var i = 0; i < c; ++i) { - var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off); - o = no; - if (!c_2) - files[fn] = slc(data, b, b + sc); - else if (c_2 == 8) - files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su)); - else - throw 'unknown compression type ' + c_2; - } - return files; - } /*! * three-tiltloader @@ -50970,8 +55545,7 @@ var WEBGL_TEXTURE_DATATYPES = { 5121: UnsignedByteType, 32819: UnsignedShort4444Type, - 32820: UnsignedShort5551Type, - 33635: UnsignedShort565Type + 32820: UnsignedShort5551Type }; var WEBGL_SIDES = { @@ -52715,7 +57289,7 @@ function replaceBrushMaterials(brushPath, model) { return __awaiter(this, void 0, void 0, function* () { - const tiltShaderLoader = new TiltShaderLoader(DefaultLoadingManager); + const tiltShaderLoader = new TiltShaderLoader$1(DefaultLoadingManager); tiltShaderLoader.setPath(brushPath); const clock = new Clock(); model.traverse((object) => __awaiter(this, void 0, void 0, function* () { diff --git a/dist/icosa-viewer.min.js b/dist/icosa-viewer.min.js index 6168b1c..9964ed0 100644 --- a/dist/icosa-viewer.min.js +++ b/dist/icosa-viewer.min.js @@ -1,7 +1,7 @@ /*! * Icosa Viewer * https://github.com/icosa-gallery/icosa-viewer - * Copyright (c) 2021 Icosa Gallery + * Copyright (c) 2021-2022 Icosa Gallery * Released under the Apache 2.0 Licence. */ (function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?factory(exports):typeof define==="function"&&define.amd?define(["exports"],factory):(global=typeof globalThis!=="undefined"?globalThis:global||self,factory(global.IcosaViewer={}))})(this,(function(exports){"use strict"; @@ -27,21 +27,27 @@ */var ACTION;(function(ACTION){ACTION[ACTION["NONE"]=0]="NONE";ACTION[ACTION["ROTATE"]=1]="ROTATE";ACTION[ACTION["TRUCK"]=2]="TRUCK";ACTION[ACTION["OFFSET"]=3]="OFFSET";ACTION[ACTION["DOLLY"]=4]="DOLLY";ACTION[ACTION["ZOOM"]=5]="ZOOM";ACTION[ACTION["TOUCH_ROTATE"]=6]="TOUCH_ROTATE";ACTION[ACTION["TOUCH_TRUCK"]=7]="TOUCH_TRUCK";ACTION[ACTION["TOUCH_OFFSET"]=8]="TOUCH_OFFSET";ACTION[ACTION["TOUCH_DOLLY"]=9]="TOUCH_DOLLY";ACTION[ACTION["TOUCH_ZOOM"]=10]="TOUCH_ZOOM";ACTION[ACTION["TOUCH_DOLLY_TRUCK"]=11]="TOUCH_DOLLY_TRUCK";ACTION[ACTION["TOUCH_DOLLY_OFFSET"]=12]="TOUCH_DOLLY_OFFSET";ACTION[ACTION["TOUCH_ZOOM_TRUCK"]=13]="TOUCH_ZOOM_TRUCK";ACTION[ACTION["TOUCH_ZOOM_OFFSET"]=14]="TOUCH_ZOOM_OFFSET"})(ACTION||(ACTION={}));function isPerspectiveCamera(camera){return camera.isPerspectiveCamera}function isOrthographicCamera(camera){return camera.isOrthographicCamera}const PI_2=Math.PI*2;const PI_HALF=Math.PI/2;const EPSILON=1e-5;function approxZero(number,error=EPSILON){return Math.abs(number){out.x+=pointer.clientX;out.y+=pointer.clientY}));out.x/=pointers.length;out.y/=pointers.length}function notSupportedInOrthographicCamera(camera,message){if(isOrthographicCamera(camera)){console.warn(`${message} is not supported in OrthographicCamera`);return true}return false}function quatInvertCompat(target){if(target.invert){target.invert()}else{target.inverse()}return target}class EventDispatcher$2{constructor(){this._listeners={}}addEventListener(type,listener){const listeners=this._listeners;if(listeners[type]===undefined)listeners[type]=[];if(listeners[type].indexOf(listener)===-1)listeners[type].push(listener)}removeEventListener(type,listener){const listeners=this._listeners;const listenerArray=listeners[type];if(listenerArray!==undefined){const index=listenerArray.indexOf(listener);if(index!==-1)listenerArray.splice(index,1)}}removeAllEventListeners(type){if(!type){this._listeners={};return}if(Array.isArray(this._listeners[type]))this._listeners[type].length=0}dispatchEvent(event){const listeners=this._listeners;const listenerArray=listeners[event.type];if(listenerArray!==undefined){event.target=this;const array=listenerArray.slice(0);for(let i=0,l=array.length;i{};this._enabled=true;this._state=ACTION.NONE;this._viewport=null;this._dollyControlAmount=0;this._hasRested=true;this._boundaryEnclosesCamera=false;this._needsUpdate=true;this._updatedLastTime=false;this._elementRect=new DOMRect;this._activePointers=[];this._truckInternal=(deltaX,deltaY,dragToOffset)=>{if(isPerspectiveCamera(this._camera)){const offset=_v3A.copy(this._camera.position).sub(this._target);const fov=this._camera.getEffectiveFOV()*THREE.MathUtils.DEG2RAD;const targetDistance=offset.length()*Math.tan(fov*.5);const truckX=this.truckSpeed*deltaX*targetDistance/this._elementRect.height;const pedestalY=this.truckSpeed*deltaY*targetDistance/this._elementRect.height;if(this.verticalDragToForward){dragToOffset?this.setFocalOffset(this._focalOffsetEnd.x+truckX,this._focalOffsetEnd.y,this._focalOffsetEnd.z,true):this.truck(truckX,0,true);this.forward(-pedestalY,true)}else{dragToOffset?this.setFocalOffset(this._focalOffsetEnd.x+truckX,this._focalOffsetEnd.y+pedestalY,this._focalOffsetEnd.z,true):this.truck(truckX,pedestalY,true)}}else if(isOrthographicCamera(this._camera)){const camera=this._camera;const truckX=deltaX*(camera.right-camera.left)/camera.zoom/this._elementRect.width;const pedestalY=deltaY*(camera.top-camera.bottom)/camera.zoom/this._elementRect.height;dragToOffset?this.setFocalOffset(this._focalOffsetEnd.x+truckX,this._focalOffsetEnd.y+pedestalY,this._focalOffsetEnd.z,true):this.truck(truckX,pedestalY,true)}};this._rotateInternal=(deltaX,deltaY)=>{const theta=PI_2*this.azimuthRotateSpeed*deltaX/this._elementRect.height;const phi=PI_2*this.polarRotateSpeed*deltaY/this._elementRect.height;this.rotate(theta,phi,true)};this._dollyInternal=(delta,x,y)=>{const dollyScale=Math.pow(.95,-delta*this.dollySpeed);const distance=this._sphericalEnd.radius*dollyScale;const prevRadius=this._sphericalEnd.radius;const signedPrevRadius=prevRadius*(delta>=0?-1:1);this.dollyTo(distance);if(this.infinityDolly&&(distance{const zoomScale=Math.pow(.95,delta*this.dollySpeed);this.zoomTo(this._zoom*zoomScale);if(this.dollyToCursor){this._dollyControlAmount=this._zoomEnd;this._dollyControlCoord.set(x,y)}return};if(typeof THREE==="undefined"){console.error("camera-controls: `THREE` is undefined. You must first run `CameraControls.install( { THREE: THREE } )`. Check the docs for further information.")}this._camera=camera;this._yAxisUpSpace=(new THREE.Quaternion).setFromUnitVectors(this._camera.up,_AXIS_Y);this._yAxisUpSpaceInverse=quatInvertCompat(this._yAxisUpSpace.clone());this._state=ACTION.NONE;this._domElement=domElement;this._domElement.style.touchAction="none";this._domElement.style.userSelect="none";this._domElement.style.webkitUserSelect="none";this._target=new THREE.Vector3;this._targetEnd=this._target.clone();this._focalOffset=new THREE.Vector3;this._focalOffsetEnd=this._focalOffset.clone();this._spherical=(new THREE.Spherical).setFromVector3(_v3A.copy(this._camera.position).applyQuaternion(this._yAxisUpSpace));this._sphericalEnd=this._spherical.clone();this._zoom=this._camera.zoom;this._zoomEnd=this._zoom;this._nearPlaneCorners=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];this._updateNearPlaneCorners();this._boundary=new THREE.Box3(new THREE.Vector3(-Infinity,-Infinity,-Infinity),new THREE.Vector3(Infinity,Infinity,Infinity));this._target0=this._target.clone();this._position0=this._camera.position.clone();this._zoom0=this._zoom;this._focalOffset0=this._focalOffset.clone();this._dollyControlAmount=0;this._dollyControlCoord=new THREE.Vector2;this.mouseButtons={left:ACTION.ROTATE,middle:ACTION.DOLLY,right:ACTION.TRUCK,wheel:isPerspectiveCamera(this._camera)?ACTION.DOLLY:isOrthographicCamera(this._camera)?ACTION.ZOOM:ACTION.NONE,shiftLeft:ACTION.NONE};this.touches={one:ACTION.TOUCH_ROTATE,two:isPerspectiveCamera(this._camera)?ACTION.TOUCH_DOLLY_TRUCK:isOrthographicCamera(this._camera)?ACTION.TOUCH_ZOOM_TRUCK:ACTION.NONE,three:ACTION.TOUCH_TRUCK};if(this._domElement){const dragStartPosition=new THREE.Vector2;const lastDragPosition=new THREE.Vector2;const dollyStart=new THREE.Vector2;const onPointerDown=event=>{if(!this._enabled)return;const pointer={pointerId:event.pointerId,clientX:event.clientX,clientY:event.clientY};this._activePointers.push(pointer);switch(event.button){case THREE.MOUSE.LEFT:this._state=event.shiftKey?this.mouseButtons.shiftLeft:this.mouseButtons.left;break;case THREE.MOUSE.MIDDLE:this._state=this.mouseButtons.middle;break;case THREE.MOUSE.RIGHT:this._state=this.mouseButtons.right;break}if(event.pointerType==="touch"){switch(this._activePointers.length){case 1:this._state=this.touches.one;break;case 2:this._state=this.touches.two;break;case 3:this._state=this.touches.three;break}}this._domElement.ownerDocument.removeEventListener("pointermove",onPointerMove,{passive:false});this._domElement.ownerDocument.removeEventListener("pointerup",onPointerUp);this._domElement.ownerDocument.addEventListener("pointermove",onPointerMove,{passive:false});this._domElement.ownerDocument.addEventListener("pointerup",onPointerUp);startDragging()};const onMouseDown=event=>{if(!this._enabled)return;const pointer={pointerId:0,clientX:event.clientX,clientY:event.clientY};this._activePointers.push(pointer);switch(event.button){case THREE.MOUSE.LEFT:this._state=event.shiftKey?this.mouseButtons.shiftLeft:this.mouseButtons.left;break;case THREE.MOUSE.MIDDLE:this._state=this.mouseButtons.middle;break;case THREE.MOUSE.RIGHT:this._state=this.mouseButtons.right;break}this._domElement.ownerDocument.removeEventListener("mousemove",onMouseMove);this._domElement.ownerDocument.removeEventListener("mouseup",onMouseUp);this._domElement.ownerDocument.addEventListener("mousemove",onMouseMove);this._domElement.ownerDocument.addEventListener("mouseup",onMouseUp);startDragging()};const onTouchStart=event=>{if(!this._enabled)return;event.preventDefault();Array.prototype.forEach.call(event.changedTouches,(touch=>{const pointer={pointerId:touch.identifier,clientX:touch.clientX,clientY:touch.clientY};this._activePointers.push(pointer)}));switch(this._activePointers.length){case 1:this._state=this.touches.one;break;case 2:this._state=this.touches.two;break;case 3:this._state=this.touches.three;break}this._domElement.ownerDocument.removeEventListener("touchmove",onTouchMove,{passive:false});this._domElement.ownerDocument.removeEventListener("touchend",onTouchEnd);this._domElement.ownerDocument.addEventListener("touchmove",onTouchMove,{passive:false});this._domElement.ownerDocument.addEventListener("touchend",onTouchEnd);startDragging()};const onPointerMove=event=>{if(event.cancelable)event.preventDefault();const pointerId=event.pointerId;const pointer=this._findPointerById(pointerId);if(!pointer)return;pointer.clientX=event.clientX;pointer.clientY=event.clientY;dragging()};const onMouseMove=event=>{const pointer=this._findPointerById(0);if(!pointer)return;pointer.clientX=event.clientX;pointer.clientY=event.clientY;dragging()};const onTouchMove=event=>{if(event.cancelable)event.preventDefault();Array.prototype.forEach.call(event.changedTouches,(touch=>{const pointerId=touch.identifier;const pointer=this._findPointerById(pointerId);if(!pointer)return;pointer.clientX=touch.clientX;pointer.clientY=touch.clientY}));dragging()};const onPointerUp=event=>{const pointerId=event.pointerId;const pointer=this._findPointerById(pointerId);pointer&&this._activePointers.splice(this._activePointers.indexOf(pointer),1);if(event.pointerType==="touch"){switch(this._activePointers.length){case 0:this._state=ACTION.NONE;break;case 1:this._state=this.touches.one;break;case 2:this._state=this.touches.two;break;case 3:this._state=this.touches.three;break}}else{this._state=ACTION.NONE}endDragging()};const onMouseUp=()=>{const pointer=this._findPointerById(0);pointer&&this._activePointers.splice(this._activePointers.indexOf(pointer),1);this._state=ACTION.NONE;endDragging()};const onTouchEnd=event=>{Array.prototype.forEach.call(event.changedTouches,(touch=>{const pointerId=touch.identifier;const pointer=this._findPointerById(pointerId);pointer&&this._activePointers.splice(this._activePointers.indexOf(pointer),1)}));switch(this._activePointers.length){case 0:this._state=ACTION.NONE;break;case 1:this._state=this.touches.one;break;case 2:this._state=this.touches.two;break;case 3:this._state=this.touches.three;break}endDragging()};let lastScrollTimeStamp=-1;const onMouseWheel=event=>{if(!this._enabled||this.mouseButtons.wheel===ACTION.NONE)return;event.preventDefault();if(this.dollyToCursor||this.mouseButtons.wheel===ACTION.ROTATE||this.mouseButtons.wheel===ACTION.TRUCK){const now=performance.now();if(lastScrollTimeStamp-now<1e3)this._getClientRect(this._elementRect);lastScrollTimeStamp=now}const deltaYFactor=isMac?-1:-3;const delta=event.deltaMode===1?event.deltaY/deltaYFactor:event.deltaY/(deltaYFactor*10);const x=this.dollyToCursor?(event.clientX-this._elementRect.x)/this._elementRect.width*2-1:0;const y=this.dollyToCursor?(event.clientY-this._elementRect.y)/this._elementRect.height*-2+1:0;switch(this.mouseButtons.wheel){case ACTION.ROTATE:{this._rotateInternal(event.deltaX,event.deltaY);break}case ACTION.TRUCK:{this._truckInternal(event.deltaX,event.deltaY,false);break}case ACTION.OFFSET:{this._truckInternal(event.deltaX,event.deltaY,true);break}case ACTION.DOLLY:{this._dollyInternal(-delta,x,y);break}case ACTION.ZOOM:{this._zoomInternal(-delta,x,y);break}}this.dispatchEvent({type:"control"})};const onContextMenu=event=>{if(!this._enabled)return;event.preventDefault()};const startDragging=()=>{if(!this._enabled)return;extractClientCoordFromEvent(this._activePointers,_v2);this._getClientRect(this._elementRect);dragStartPosition.copy(_v2);lastDragPosition.copy(_v2);const isMultiTouch=this._activePointers.length>=2;if(isMultiTouch){const dx=_v2.x-this._activePointers[1].clientX;const dy=_v2.y-this._activePointers[1].clientY;const distance=Math.sqrt(dx*dx+dy*dy);dollyStart.set(0,distance);const x=(this._activePointers[0].clientX+this._activePointers[1].clientX)*.5;const y=(this._activePointers[0].clientY+this._activePointers[1].clientY)*.5;lastDragPosition.set(x,y)}this.dispatchEvent({type:"controlstart"})};const dragging=()=>{if(!this._enabled)return;extractClientCoordFromEvent(this._activePointers,_v2);const deltaX=lastDragPosition.x-_v2.x;const deltaY=lastDragPosition.y-_v2.y;lastDragPosition.copy(_v2);switch(this._state){case ACTION.ROTATE:case ACTION.TOUCH_ROTATE:{this._rotateInternal(deltaX,deltaY);break}case ACTION.DOLLY:case ACTION.ZOOM:{const dollyX=this.dollyToCursor?(dragStartPosition.x-this._elementRect.x)/this._elementRect.width*2-1:0;const dollyY=this.dollyToCursor?(dragStartPosition.y-this._elementRect.y)/this._elementRect.height*-2+1:0;this._state===ACTION.DOLLY?this._dollyInternal(deltaY*TOUCH_DOLLY_FACTOR,dollyX,dollyY):this._zoomInternal(deltaY*TOUCH_DOLLY_FACTOR,dollyX,dollyY);break}case ACTION.TOUCH_DOLLY:case ACTION.TOUCH_ZOOM:case ACTION.TOUCH_DOLLY_TRUCK:case ACTION.TOUCH_ZOOM_TRUCK:case ACTION.TOUCH_DOLLY_OFFSET:case ACTION.TOUCH_ZOOM_OFFSET:{const dx=_v2.x-this._activePointers[1].clientX;const dy=_v2.y-this._activePointers[1].clientY;const distance=Math.sqrt(dx*dx+dy*dy);const dollyDelta=dollyStart.y-distance;dollyStart.set(0,distance);const dollyX=this.dollyToCursor?(lastDragPosition.x-this._elementRect.x)/this._elementRect.width*2-1:0;const dollyY=this.dollyToCursor?(lastDragPosition.y-this._elementRect.y)/this._elementRect.height*-2+1:0;this._state===ACTION.TOUCH_DOLLY||this._state===ACTION.TOUCH_DOLLY_TRUCK||this._state===ACTION.TOUCH_DOLLY_OFFSET?this._dollyInternal(dollyDelta*TOUCH_DOLLY_FACTOR,dollyX,dollyY):this._zoomInternal(dollyDelta*TOUCH_DOLLY_FACTOR,dollyX,dollyY);if(this._state===ACTION.TOUCH_DOLLY_TRUCK||this._state===ACTION.TOUCH_ZOOM_TRUCK){this._truckInternal(deltaX,deltaY,false)}else if(this._state===ACTION.TOUCH_DOLLY_OFFSET||this._state===ACTION.TOUCH_ZOOM_OFFSET){this._truckInternal(deltaX,deltaY,true)}break}case ACTION.TRUCK:case ACTION.TOUCH_TRUCK:{this._truckInternal(deltaX,deltaY,false);break}case ACTION.OFFSET:case ACTION.TOUCH_OFFSET:{this._truckInternal(deltaX,deltaY,true);break}}this.dispatchEvent({type:"control"})};const endDragging=()=>{extractClientCoordFromEvent(this._activePointers,_v2);lastDragPosition.copy(_v2);if(this._activePointers.length===0){this._domElement.ownerDocument.removeEventListener("pointermove",onPointerMove,{passive:false});this._domElement.ownerDocument.removeEventListener("pointerup",onPointerUp);this._domElement.ownerDocument.removeEventListener("touchmove",onTouchMove,{passive:false});this._domElement.ownerDocument.removeEventListener("touchend",onTouchEnd);this.dispatchEvent({type:"controlend"})}};this._domElement.addEventListener("pointerdown",onPointerDown);isPointerEventsNotSupported&&this._domElement.addEventListener("mousedown",onMouseDown);isPointerEventsNotSupported&&this._domElement.addEventListener("touchstart",onTouchStart);this._domElement.addEventListener("pointercancel",onPointerUp);this._domElement.addEventListener("wheel",onMouseWheel,{passive:false});this._domElement.addEventListener("contextmenu",onContextMenu);this._removeAllEventListeners=()=>{this._domElement.removeEventListener("pointerdown",onPointerDown);this._domElement.removeEventListener("mousedown",onMouseDown);this._domElement.removeEventListener("touchstart",onTouchStart);this._domElement.removeEventListener("pointercancel",onPointerUp);this._domElement.removeEventListener("wheel",onMouseWheel,{passive:false});this._domElement.removeEventListener("contextmenu",onContextMenu);this._domElement.ownerDocument.removeEventListener("pointermove",onPointerMove,{passive:false});this._domElement.ownerDocument.removeEventListener("mousemove",onMouseMove);this._domElement.ownerDocument.removeEventListener("touchmove",onTouchMove,{passive:false});this._domElement.ownerDocument.removeEventListener("pointerup",onPointerUp);this._domElement.ownerDocument.removeEventListener("mouseup",onMouseUp);this._domElement.ownerDocument.removeEventListener("touchend",onTouchEnd)};this.cancel=()=>{if(this._state===ACTION.NONE)return;this._state=ACTION.NONE;this._activePointers.length=0;endDragging()}}this.update(0)}static install(libs){THREE=libs.THREE;_ORIGIN=Object.freeze(new THREE.Vector3(0,0,0));_AXIS_Y=Object.freeze(new THREE.Vector3(0,1,0));_AXIS_Z=Object.freeze(new THREE.Vector3(0,0,1));_v2=new THREE.Vector2;_v3A=new THREE.Vector3;_v3B=new THREE.Vector3;_v3C=new THREE.Vector3;_xColumn=new THREE.Vector3;_yColumn=new THREE.Vector3;_zColumn=new THREE.Vector3;_sphericalA=new THREE.Spherical;_sphericalB=new THREE.Spherical;_box3A=new THREE.Box3;_box3B=new THREE.Box3;_sphere$4=new THREE.Sphere;_quaternionA=new THREE.Quaternion;_quaternionB=new THREE.Quaternion;_rotationMatrix=new THREE.Matrix4;_raycaster=new THREE.Raycaster}static get ACTION(){return readonlyACTION}get camera(){return this._camera}set camera(camera){this._camera=camera;this.updateCameraUp();this._camera.updateProjectionMatrix();this._updateNearPlaneCorners();this._needsUpdate=true}get enabled(){return this._enabled}set enabled(enabled){this._enabled=enabled;if(enabled){this._domElement.style.touchAction="none";this._domElement.style.userSelect="none";this._domElement.style.webkitUserSelect="none"}else{this.cancel();this._domElement.style.touchAction="";this._domElement.style.userSelect="";this._domElement.style.webkitUserSelect=""}}get active(){return!this._hasRested}get currentAction(){return this._state}get distance(){return this._spherical.radius}set distance(distance){if(this._spherical.radius===distance&&this._sphericalEnd.radius===distance)return;this._spherical.radius=distance;this._sphericalEnd.radius=distance;this._needsUpdate=true}get azimuthAngle(){return this._spherical.theta}set azimuthAngle(azimuthAngle){if(this._spherical.theta===azimuthAngle&&this._sphericalEnd.theta===azimuthAngle)return;this._spherical.theta=azimuthAngle;this._sphericalEnd.theta=azimuthAngle;this._needsUpdate=true}get polarAngle(){return this._spherical.phi}set polarAngle(polarAngle){if(this._spherical.phi===polarAngle&&this._sphericalEnd.phi===polarAngle)return;this._spherical.phi=polarAngle;this._sphericalEnd.phi=polarAngle;this._needsUpdate=true}get boundaryEnclosesCamera(){return this._boundaryEnclosesCamera}set boundaryEnclosesCamera(boundaryEnclosesCamera){this._boundaryEnclosesCamera=boundaryEnclosesCamera;this._needsUpdate=true}addEventListener(type,listener){super.addEventListener(type,listener)}removeEventListener(type,listener){super.removeEventListener(type,listener)}rotate(azimuthAngle,polarAngle,enableTransition=false){return this.rotateTo(this._sphericalEnd.theta+azimuthAngle,this._sphericalEnd.phi+polarAngle,enableTransition)}rotateAzimuthTo(azimuthAngle,enableTransition=false){return this.rotateTo(azimuthAngle,this._sphericalEnd.phi,enableTransition)}rotatePolarTo(polarAngle,enableTransition=false){return this.rotateTo(this._sphericalEnd.theta,polarAngle,enableTransition)}rotateTo(azimuthAngle,polarAngle,enableTransition=false){const theta=THREE.MathUtils.clamp(azimuthAngle,this.minAzimuthAngle,this.maxAzimuthAngle);const phi=THREE.MathUtils.clamp(polarAngle,this.minPolarAngle,this.maxPolarAngle);this._sphericalEnd.theta=theta;this._sphericalEnd.phi=phi;this._sphericalEnd.makeSafe();this._needsUpdate=true;if(!enableTransition){this._spherical.theta=this._sphericalEnd.theta;this._spherical.phi=this._sphericalEnd.phi}const resolveImmediately=!enableTransition||approxEquals(this._spherical.theta,this._sphericalEnd.theta,this.restThreshold)&&approxEquals(this._spherical.phi,this._sphericalEnd.phi,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}dolly(distance,enableTransition=false){return this.dollyTo(this._sphericalEnd.radius-distance,enableTransition)}dollyTo(distance,enableTransition=false){const lastRadius=this._sphericalEnd.radius;const newRadius=THREE.MathUtils.clamp(distance,this.minDistance,this.maxDistance);const hasCollider=this.colliderMeshes.length>=1;if(hasCollider){const maxDistanceByCollisionTest=this._collisionTest();const isCollided=approxEquals(maxDistanceByCollisionTest,this._spherical.radius);const isDollyIn=lastRadius>newRadius;if(!isDollyIn&&isCollided)return Promise.resolve();this._sphericalEnd.radius=Math.min(newRadius,maxDistanceByCollisionTest)}else{this._sphericalEnd.radius=newRadius}this._needsUpdate=true;if(!enableTransition){this._spherical.radius=this._sphericalEnd.radius}const resolveImmediately=!enableTransition||approxEquals(this._spherical.radius,this._sphericalEnd.radius,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}zoom(zoomStep,enableTransition=false){return this.zoomTo(this._zoomEnd+zoomStep,enableTransition)}zoomTo(zoom,enableTransition=false){this._zoomEnd=THREE.MathUtils.clamp(zoom,this.minZoom,this.maxZoom);this._needsUpdate=true;if(!enableTransition){this._zoom=this._zoomEnd}const resolveImmediately=!enableTransition||approxEquals(this._zoom,this._zoomEnd,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}pan(x,y,enableTransition=false){console.warn("`pan` has been renamed to `truck`");return this.truck(x,y,enableTransition)}truck(x,y,enableTransition=false){this._camera.updateMatrix();_xColumn.setFromMatrixColumn(this._camera.matrix,0);_yColumn.setFromMatrixColumn(this._camera.matrix,1);_xColumn.multiplyScalar(x);_yColumn.multiplyScalar(-y);const offset=_v3A.copy(_xColumn).add(_yColumn);const to=_v3B.copy(this._targetEnd).add(offset);return this.moveTo(to.x,to.y,to.z,enableTransition)}forward(distance,enableTransition=false){_v3A.setFromMatrixColumn(this._camera.matrix,0);_v3A.crossVectors(this._camera.up,_v3A);_v3A.multiplyScalar(distance);const to=_v3B.copy(this._targetEnd).add(_v3A);return this.moveTo(to.x,to.y,to.z,enableTransition)}moveTo(x,y,z,enableTransition=false){const offset=_v3A.set(x,y,z).sub(this._targetEnd);this._encloseToBoundary(this._targetEnd,offset,this.boundaryFriction);this._needsUpdate=true;if(!enableTransition){this._target.copy(this._targetEnd)}const resolveImmediately=!enableTransition||approxEquals(this._target.x,this._targetEnd.x,this.restThreshold)&&approxEquals(this._target.y,this._targetEnd.y,this.restThreshold)&&approxEquals(this._target.z,this._targetEnd.z,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}fitToBox(box3OrObject,enableTransition,{paddingLeft:paddingLeft=0,paddingRight:paddingRight=0,paddingBottom:paddingBottom=0,paddingTop:paddingTop=0}={}){const promises=[];const aabb=box3OrObject.isBox3?_box3A.copy(box3OrObject):_box3A.setFromObject(box3OrObject);if(aabb.isEmpty()){console.warn("camera-controls: fitTo() cannot be used with an empty box. Aborting");Promise.resolve()}const theta=roundToStep(this._sphericalEnd.theta,PI_HALF);const phi=roundToStep(this._sphericalEnd.phi,PI_HALF);promises.push(this.rotateTo(theta,phi,enableTransition));const normal=_v3A.setFromSpherical(this._sphericalEnd).normalize();const rotation=_quaternionA.setFromUnitVectors(normal,_AXIS_Z);const viewFromPolar=approxEquals(Math.abs(normal.y),1);if(viewFromPolar){rotation.multiply(_quaternionB.setFromAxisAngle(_AXIS_Y,theta))}const bb=_box3B.makeEmpty();_v3B.copy(aabb.min).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.min).setX(aabb.max.x).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.min).setY(aabb.max.y).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.max).setZ(aabb.min.z).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.min).setZ(aabb.max.z).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.max).setY(aabb.min.y).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.max).setX(aabb.min.x).applyQuaternion(rotation);bb.expandByPoint(_v3B);_v3B.copy(aabb.max).applyQuaternion(rotation);bb.expandByPoint(_v3B);rotation.setFromUnitVectors(_AXIS_Z,normal);bb.min.x-=paddingLeft;bb.min.y-=paddingBottom;bb.max.x+=paddingRight;bb.max.y+=paddingTop;const bbSize=bb.getSize(_v3A);const center=bb.getCenter(_v3B).applyQuaternion(rotation);if(isPerspectiveCamera(this._camera)){const distance=this.getDistanceToFitBox(bbSize.x,bbSize.y,bbSize.z);promises.push(this.moveTo(center.x,center.y,center.z,enableTransition));promises.push(this.dollyTo(distance,enableTransition));promises.push(this.setFocalOffset(0,0,0,enableTransition))}else if(isOrthographicCamera(this._camera)){const camera=this._camera;const width=camera.right-camera.left;const height=camera.top-camera.bottom;const zoom=Math.min(width/bbSize.x,height/bbSize.y);promises.push(this.moveTo(center.x,center.y,center.z,enableTransition));promises.push(this.zoomTo(zoom,enableTransition));promises.push(this.setFocalOffset(0,0,0,enableTransition))}return Promise.all(promises)}fitTo(box3OrObject,enableTransition,fitToOptions={}){console.warn("camera-controls: fitTo() has been renamed to fitToBox()");return this.fitToBox(box3OrObject,enableTransition,fitToOptions)}fitToSphere(sphereOrMesh,enableTransition){const promises=[];const isSphere=sphereOrMesh instanceof THREE.Sphere;const boundingSphere=isSphere?_sphere$4.copy(sphereOrMesh):createBoundingSphere(sphereOrMesh,_sphere$4);promises.push(this.moveTo(boundingSphere.center.x,boundingSphere.center.y,boundingSphere.center.z,enableTransition));if(isPerspectiveCamera(this._camera)){const distanceToFit=this.getDistanceToFitSphere(boundingSphere.radius);promises.push(this.dollyTo(distanceToFit,enableTransition))}else if(isOrthographicCamera(this._camera)){const width=this._camera.right-this._camera.left;const height=this._camera.top-this._camera.bottom;const diameter=2*boundingSphere.radius;const zoom=Math.min(width/diameter,height/diameter);promises.push(this.zoomTo(zoom,enableTransition))}promises.push(this.setFocalOffset(0,0,0,enableTransition));return Promise.all(promises)}setLookAt(positionX,positionY,positionZ,targetX,targetY,targetZ,enableTransition=false){const target=_v3B.set(targetX,targetY,targetZ);const position=_v3A.set(positionX,positionY,positionZ);this._targetEnd.copy(target);this._sphericalEnd.setFromVector3(position.sub(target).applyQuaternion(this._yAxisUpSpace));this.normalizeRotations();this._needsUpdate=true;if(!enableTransition){this._target.copy(this._targetEnd);this._spherical.copy(this._sphericalEnd)}const resolveImmediately=!enableTransition||approxEquals(this._target.x,this._targetEnd.x,this.restThreshold)&&approxEquals(this._target.y,this._targetEnd.y,this.restThreshold)&&approxEquals(this._target.z,this._targetEnd.z,this.restThreshold)&&approxEquals(this._spherical.theta,this._sphericalEnd.theta,this.restThreshold)&&approxEquals(this._spherical.phi,this._sphericalEnd.phi,this.restThreshold)&&approxEquals(this._spherical.radius,this._sphericalEnd.radius,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}lerpLookAt(positionAX,positionAY,positionAZ,targetAX,targetAY,targetAZ,positionBX,positionBY,positionBZ,targetBX,targetBY,targetBZ,t,enableTransition=false){const targetA=_v3A.set(targetAX,targetAY,targetAZ);const positionA=_v3B.set(positionAX,positionAY,positionAZ);_sphericalA.setFromVector3(positionA.sub(targetA).applyQuaternion(this._yAxisUpSpace));const targetB=_v3C.set(targetBX,targetBY,targetBZ);const positionB=_v3B.set(positionBX,positionBY,positionBZ);_sphericalB.setFromVector3(positionB.sub(targetB).applyQuaternion(this._yAxisUpSpace));this._targetEnd.copy(targetA.lerp(targetB,t));const deltaTheta=_sphericalB.theta-_sphericalA.theta;const deltaPhi=_sphericalB.phi-_sphericalA.phi;const deltaRadius=_sphericalB.radius-_sphericalA.radius;this._sphericalEnd.set(_sphericalA.radius+deltaRadius*t,_sphericalA.phi+deltaPhi*t,_sphericalA.theta+deltaTheta*t);this.normalizeRotations();this._needsUpdate=true;if(!enableTransition){this._target.copy(this._targetEnd);this._spherical.copy(this._sphericalEnd)}const resolveImmediately=!enableTransition||approxEquals(this._target.x,this._targetEnd.x,this.restThreshold)&&approxEquals(this._target.y,this._targetEnd.y,this.restThreshold)&&approxEquals(this._target.z,this._targetEnd.z,this.restThreshold)&&approxEquals(this._spherical.theta,this._sphericalEnd.theta,this.restThreshold)&&approxEquals(this._spherical.phi,this._sphericalEnd.phi,this.restThreshold)&&approxEquals(this._spherical.radius,this._sphericalEnd.radius,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}setPosition(positionX,positionY,positionZ,enableTransition=false){return this.setLookAt(positionX,positionY,positionZ,this._targetEnd.x,this._targetEnd.y,this._targetEnd.z,enableTransition)}setTarget(targetX,targetY,targetZ,enableTransition=false){const pos=this.getPosition(_v3A);return this.setLookAt(pos.x,pos.y,pos.z,targetX,targetY,targetZ,enableTransition)}setFocalOffset(x,y,z,enableTransition=false){this._focalOffsetEnd.set(x,y,z);this._needsUpdate=true;if(!enableTransition){this._focalOffset.copy(this._focalOffsetEnd)}const resolveImmediately=!enableTransition||approxEquals(this._focalOffset.x,this._focalOffsetEnd.x,this.restThreshold)&&approxEquals(this._focalOffset.y,this._focalOffsetEnd.y,this.restThreshold)&&approxEquals(this._focalOffset.z,this._focalOffsetEnd.z,this.restThreshold);return this._createOnRestPromise(resolveImmediately)}setOrbitPoint(targetX,targetY,targetZ){_xColumn.setFromMatrixColumn(this._camera.matrixWorldInverse,0);_yColumn.setFromMatrixColumn(this._camera.matrixWorldInverse,1);_zColumn.setFromMatrixColumn(this._camera.matrixWorldInverse,2);const position=_v3A.set(targetX,targetY,targetZ);const distance=position.distanceTo(this._camera.position);const cameraToPoint=position.sub(this._camera.position);_xColumn.multiplyScalar(cameraToPoint.x);_yColumn.multiplyScalar(cameraToPoint.y);_zColumn.multiplyScalar(cameraToPoint.z);_v3A.copy(_xColumn).add(_yColumn).add(_zColumn);_v3A.z=_v3A.z+distance;this.dollyTo(distance,false);this.setFocalOffset(-_v3A.x,_v3A.y,-_v3A.z,false);this.moveTo(targetX,targetY,targetZ,false)}setBoundary(box3){if(!box3){this._boundary.min.set(-Infinity,-Infinity,-Infinity);this._boundary.max.set(Infinity,Infinity,Infinity);this._needsUpdate=true;return}this._boundary.copy(box3);this._boundary.clampPoint(this._targetEnd,this._targetEnd);this._needsUpdate=true}setViewport(viewportOrX,y,width,height){if(viewportOrX===null){this._viewport=null;return}this._viewport=this._viewport||new THREE.Vector4;if(typeof viewportOrX==="number"){this._viewport.set(viewportOrX,y,width,height)}else{this._viewport.copy(viewportOrX)}}getDistanceToFitBox(width,height,depth){if(notSupportedInOrthographicCamera(this._camera,"getDistanceToFitBox"))return this._spherical.radius;const boundingRectAspect=width/height;const fov=this._camera.getEffectiveFOV()*THREE.MathUtils.DEG2RAD;const aspect=this._camera.aspect;const heightToFit=boundingRectAspect{if(activePointer.pointerId===pointerId){pointer=activePointer;return true}return false}));return pointer}_encloseToBoundary(position,offset,friction){const offsetLength2=offset.lengthSq();if(offsetLength2===0){return position}const newTarget=_v3B.copy(offset).add(position);const clampedTarget=this._boundary.clampPoint(newTarget,_v3C);const deltaClampedTarget=clampedTarget.sub(newTarget);const deltaClampedTargetLength2=deltaClampedTarget.lengthSq();if(deltaClampedTargetLength2===0){return position.add(offset)}else if(deltaClampedTargetLength2===offsetLength2){return position}else if(friction===0){return position.add(offset).add(deltaClampedTarget)}else{const offsetFactor=1+friction*deltaClampedTargetLength2/offset.dot(deltaClampedTarget);return position.add(_v3B.copy(offset).multiplyScalar(offsetFactor)).add(deltaClampedTarget.multiplyScalar(1-friction))}}_updateNearPlaneCorners(){if(isPerspectiveCamera(this._camera)){const camera=this._camera;const near=camera.near;const fov=camera.getEffectiveFOV()*THREE.MathUtils.DEG2RAD;const heightHalf=Math.tan(fov*.5)*near;const widthHalf=heightHalf*camera.aspect;this._nearPlaneCorners[0].set(-widthHalf,-heightHalf,0);this._nearPlaneCorners[1].set(widthHalf,-heightHalf,0);this._nearPlaneCorners[2].set(widthHalf,heightHalf,0);this._nearPlaneCorners[3].set(-widthHalf,heightHalf,0)}else if(isOrthographicCamera(this._camera)){const camera=this._camera;const zoomInv=1/camera.zoom;const left=camera.left*zoomInv;const right=camera.right*zoomInv;const top=camera.top*zoomInv;const bottom=camera.bottom*zoomInv;this._nearPlaneCorners[0].set(left,top,0);this._nearPlaneCorners[1].set(right,top,0);this._nearPlaneCorners[2].set(right,bottom,0);this._nearPlaneCorners[3].set(left,bottom,0)}}_collisionTest(){let distance=Infinity;const hasCollider=this.colliderMeshes.length>=1;if(!hasCollider)return distance;if(notSupportedInOrthographicCamera(this._camera,"_collisionTest"))return distance;const direction=_v3A.setFromSpherical(this._spherical).divideScalar(this._spherical.radius);_rotationMatrix.lookAt(_ORIGIN,direction,this._camera.up);for(let i=0;i<4;i++){const nearPlaneCorner=_v3B.copy(this._nearPlaneCorners[i]);nearPlaneCorner.applyMatrix4(_rotationMatrix);const origin=_v3C.addVectors(this._target,nearPlaneCorner);_raycaster.set(origin,direction);_raycaster.far=this._spherical.radius+1;const intersects=_raycaster.intersectObjects(this.colliderMeshes);if(intersects.length!==0&&intersects[0].distance{const onResolve=()=>{this.removeEventListener("rest",onResolve);resolve()};this.addEventListener("rest",onResolve)}))}_removeAllEventListeners(){}}function createBoundingSphere(object3d,out){const boundingSphere=out;const center=boundingSphere.center;_box3A.makeEmpty();object3d.traverseVisible((object=>{if(!object.isMesh)return;_box3A.expandByObject(object)}));_box3A.getCenter(center);let maxRadiusSq=0;object3d.traverseVisible((object=>{if(!object.isMesh)return;const mesh=object;const geometry=mesh.geometry.clone();geometry.applyMatrix4(mesh.matrixWorld);if(geometry.isBufferGeometry){const bufferGeometry=geometry;const position=bufferGeometry.attributes.position;for(let i=0,l=position.count;i>8&255]+_lut[d0>>16&255]+_lut[d0>>24&255]+"-"+_lut[d1&255]+_lut[d1>>8&255]+"-"+_lut[d1>>16&15|64]+_lut[d1>>24&255]+"-"+_lut[d2&63|128]+_lut[d2>>8&255]+"-"+_lut[d2>>16&255]+_lut[d2>>24&255]+_lut[d3&255]+_lut[d3>>8&255]+_lut[d3>>16&255]+_lut[d3>>24&255];return uuid.toUpperCase()}function clamp(value,min,max){return Math.max(min,Math.min(max,value))}function euclideanModulo(n,m){return(n%m+m)%m}function mapLinear(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)}function inverseLerp(x,y,value){if(x!==y){return(value-x)/(y-x)}else{return 0}}function lerp(x,y,t){return(1-t)*x+t*y}function damp(x,y,lambda,dt){return lerp(x,y,1-Math.exp(-lambda*dt))}function pingpong(x,length=1){return length-Math.abs(euclideanModulo(x,length*2)-length)}function smoothstep(x,min,max){if(x<=min)return 0;if(x>=max)return 1;x=(x-min)/(max-min);return x*x*(3-2*x)}function smootherstep(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)}function randInt(low,high){return low+Math.floor(Math.random()*(high-low+1))}function randFloat(low,high){return low+Math.random()*(high-low)}function randFloatSpread(range){return range*(.5-Math.random())}function seededRandom(s){if(s!==undefined)_seed=s%2147483647;_seed=_seed*16807%2147483647;return(_seed-1)/2147483646}function degToRad(degrees){return degrees*DEG2RAD}function radToDeg(radians){return radians*RAD2DEG}function isPowerOfTwo(value){return(value&value-1)===0&&value!==0}function ceilPowerOfTwo(value){return Math.pow(2,Math.ceil(Math.log(value)/Math.LN2))}function floorPowerOfTwo(value){return Math.pow(2,Math.floor(Math.log(value)/Math.LN2))}function setQuaternionFromProperEuler(q,a,b,c,order){const cos=Math.cos;const sin=Math.sin;const c2=cos(b/2);const s2=sin(b/2);const c13=cos((a+c)/2);const s13=sin((a+c)/2);const c1_3=cos((a-c)/2);const s1_3=sin((a-c)/2);const c3_1=cos((c-a)/2);const s3_1=sin((c-a)/2);switch(order){case"XYX":q.set(c2*s13,s2*c1_3,s2*s1_3,c2*c13);break;case"YZY":q.set(s2*s1_3,c2*s13,s2*c1_3,c2*c13);break;case"ZXZ":q.set(s2*c1_3,s2*s1_3,c2*s13,c2*c13);break;case"XZX":q.set(c2*s13,s2*s3_1,s2*c3_1,c2*c13);break;case"YXY":q.set(s2*c3_1,c2*s13,s2*s3_1,c2*c13);break;case"ZYZ":q.set(s2*s3_1,s2*c3_1,c2*s13,c2*c13);break;default:console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+order)}}var MathUtils=Object.freeze({__proto__:null,DEG2RAD:DEG2RAD,RAD2DEG:RAD2DEG,generateUUID:generateUUID,clamp:clamp,euclideanModulo:euclideanModulo,mapLinear:mapLinear,inverseLerp:inverseLerp,lerp:lerp,damp:damp,pingpong:pingpong,smoothstep:smoothstep,smootherstep:smootherstep,randInt:randInt,randFloat:randFloat,randFloatSpread:randFloatSpread,seededRandom:seededRandom,degToRad:degToRad,radToDeg:radToDeg,isPowerOfTwo:isPowerOfTwo,ceilPowerOfTwo:ceilPowerOfTwo,floorPowerOfTwo:floorPowerOfTwo,setQuaternionFromProperEuler:setQuaternionFromProperEuler});class Vector2{constructor(x=0,y=0){this.x=x;this.y=y}get width(){return this.x}set width(value){this.x=value}get height(){return this.y}set height(value){this.y=value}set(x,y){this.x=x;this.y=y;return this}setScalar(scalar){this.x=scalar;this.y=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setComponent(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(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}}clone(){return new this.constructor(this.x,this.y)}copy(v){this.x=v.x;this.y=v.y;return this}add(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x+=s;this.y+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x-=s;this.y-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this}multiply(v){this.x*=v.x;this.y*=v.y;return this}multiplyScalar(scalar){this.x*=scalar;this.y*=scalar;return this}divide(v){this.x/=v.x;this.y/=v.y;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}applyMatrix3(m){const x=this.x,y=this.y;const 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(v){this.x=Math.min(this.x,v.x);this.y=Math.min(this.y,v.y);return this}max(v){this.x=Math.max(this.x,v.x);this.y=Math.max(this.y,v.y);return this}clamp(min,max){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(minVal,maxVal){this.x=Math.max(minVal,Math.min(maxVal,this.x));this.y=Math.max(minVal,Math.min(maxVal,this.y));return this}clampLength(min,max){const length=this.length();return this.divideScalar(length||1).multiplyScalar(Math.max(min,Math.min(max,length)))}floor(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this}ceil(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this}round(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}roundToZero(){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(){this.x=-this.x;this.y=-this.y;return this}dot(v){return this.x*v.x+this.y*v.y}cross(v){return this.x*v.y-this.y*v.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){const angle=Math.atan2(-this.y,-this.x)+Math.PI;return angle}distanceTo(v){return Math.sqrt(this.distanceToSquared(v))}distanceToSquared(v){const dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy}manhattanDistanceTo(v){return Math.abs(this.x-v.x)+Math.abs(this.y-v.y)}setLength(length){return this.normalize().multiplyScalar(length)}lerp(v,alpha){this.x+=(v.x-this.x)*alpha;this.y+=(v.y-this.y)*alpha;return this}lerpVectors(v1,v2,alpha){this.x=v1.x+(v2.x-v1.x)*alpha;this.y=v1.y+(v2.y-v1.y)*alpha;return this}equals(v){return v.x===this.x&&v.y===this.y}fromArray(array,offset=0){this.x=array[offset];this.y=array[offset+1];return this}toArray(array=[],offset=0){array[offset]=this.x;array[offset+1]=this.y;return array}fromBufferAttribute(attribute,index,offset){if(offset!==undefined){console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute().")}this.x=attribute.getX(index);this.y=attribute.getY(index);return this}rotateAround(center,angle){const c=Math.cos(angle),s=Math.sin(angle);const x=this.x-center.x;const y=this.y-center.y;this.x=x*c-y*s+center.x;this.y=x*s+y*c+center.y;return this}random(){this.x=Math.random();this.y=Math.random();return this}}Vector2.prototype.isVector2=true;class Matrix3{constructor(){this.elements=[1,0,0,0,1,0,0,0,1];if(arguments.length>0){console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")}}set(n11,n12,n13,n21,n22,n23,n31,n32,n33){const 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(){this.set(1,0,0,0,1,0,0,0,1);return this}copy(m){const te=this.elements;const 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}extractBasis(xAxis,yAxis,zAxis){xAxis.setFromMatrix3Column(this,0);yAxis.setFromMatrix3Column(this,1);zAxis.setFromMatrix3Column(this,2);return this}setFromMatrix4(m){const 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}multiply(m){return this.multiplyMatrices(this,m)}premultiply(m){return this.multiplyMatrices(m,this)}multiplyMatrices(a,b){const ae=a.elements;const be=b.elements;const te=this.elements;const a11=ae[0],a12=ae[3],a13=ae[6];const a21=ae[1],a22=ae[4],a23=ae[7];const a31=ae[2],a32=ae[5],a33=ae[8];const b11=be[0],b12=be[3],b13=be[6];const b21=be[1],b22=be[4],b23=be[7];const 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(s){const 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(){const te=this.elements;const 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}invert(){const te=this.elements,n11=te[0],n21=te[1],n31=te[2],n12=te[3],n22=te[4],n32=te[5],n13=te[6],n23=te[7],n33=te[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)return this.set(0,0,0,0,0,0,0,0,0);const 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(){let tmp;const 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(matrix4){return this.setFromMatrix4(matrix4).invert().transpose()}transposeIntoArray(r){const 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(tx,ty,sx,sy,rotation,cx,cy){const c=Math.cos(rotation);const 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);return this}scale(sx,sy){const te=this.elements;te[0]*=sx;te[3]*=sx;te[6]*=sx;te[1]*=sy;te[4]*=sy;te[7]*=sy;return this}rotate(theta){const c=Math.cos(theta);const s=Math.sin(theta);const te=this.elements;const a11=te[0],a12=te[3],a13=te[6];const 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(tx,ty){const 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(matrix){const te=this.elements;const me=matrix.elements;for(let i=0;i<9;i++){if(te[i]!==me[i])return false}return true}fromArray(array,offset=0){for(let i=0;i<9;i++){this.elements[i]=array[i+offset]}return this}toArray(array=[],offset=0){const 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}clone(){return(new this.constructor).fromArray(this.elements)}}Matrix3.prototype.isMatrix3=true;let _canvas;class ImageUtils{static getDataURL(image){if(/^data:/i.test(image.src)){return image.src}if(typeof HTMLCanvasElement=="undefined"){return image.src}let canvas;if(image instanceof HTMLCanvasElement){canvas=image}else{if(_canvas===undefined)_canvas=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");_canvas.width=image.width;_canvas.height=image.height;const context=_canvas.getContext("2d");if(image instanceof ImageData){context.putImageData(image,0,0)}else{context.drawImage(image,0,0,image.width,image.height)}canvas=_canvas}if(canvas.width>2048||canvas.height>2048){console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons",image);return canvas.toDataURL("image/jpeg",.6)}else{return canvas.toDataURL("image/png")}}}let textureId=0;class Texture extends EventDispatcher$1{constructor(image=Texture.DEFAULT_IMAGE,mapping=Texture.DEFAULT_MAPPING,wrapS=ClampToEdgeWrapping,wrapT=ClampToEdgeWrapping,magFilter=LinearFilter,minFilter=LinearMipmapLinearFilter,format=RGBAFormat,type=UnsignedByteType,anisotropy=1,encoding=LinearEncoding){super();Object.defineProperty(this,"id",{value:textureId++});this.uuid=generateUUID();this.name="";this.image=image;this.mipmaps=[];this.mapping=mapping;this.wrapS=wrapS;this.wrapT=wrapT;this.magFilter=magFilter;this.minFilter=minFilter;this.anisotropy=anisotropy;this.format=format;this.internalFormat=null;this.type=type;this.offset=new Vector2(0,0);this.repeat=new Vector2(1,1);this.center=new Vector2(0,0);this.rotation=0;this.matrixAutoUpdate=true;this.matrix=new Matrix3;this.generateMipmaps=true;this.premultiplyAlpha=false;this.flipY=true;this.unpackAlignment=4;this.encoding=encoding;this.version=0;this.onUpdate=null}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}clone(){return(new this.constructor).copy(this)}copy(source){this.name=source.name;this.image=source.image;this.mipmaps=source.mipmaps.slice(0);this.mapping=source.mapping;this.wrapS=source.wrapS;this.wrapT=source.wrapT;this.magFilter=source.magFilter;this.minFilter=source.minFilter;this.anisotropy=source.anisotropy;this.format=source.format;this.internalFormat=source.internalFormat;this.type=source.type;this.offset.copy(source.offset);this.repeat.copy(source.repeat);this.center.copy(source.center);this.rotation=source.rotation;this.matrixAutoUpdate=source.matrixAutoUpdate;this.matrix.copy(source.matrix);this.generateMipmaps=source.generateMipmaps;this.premultiplyAlpha=source.premultiplyAlpha;this.flipY=source.flipY;this.unpackAlignment=source.unpackAlignment;this.encoding=source.encoding;return this}toJSON(meta){const isRootObject=meta===undefined||typeof meta==="string";if(!isRootObject&&meta.textures[this.uuid]!==undefined){return meta.textures[this.uuid]}const output={metadata:{version:4.5,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,type:this.type,encoding:this.encoding,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};if(this.image!==undefined){const image=this.image;if(image.uuid===undefined){image.uuid=generateUUID()}if(!isRootObject&&meta.images[image.uuid]===undefined){let url;if(Array.isArray(image)){url=[];for(let i=0,l=image.length;i1){switch(this.wrapS){case RepeatWrapping:uv.x=uv.x-Math.floor(uv.x);break;case ClampToEdgeWrapping:uv.x=uv.x<0?0:1;break;case MirroredRepeatWrapping:if(Math.abs(Math.floor(uv.x)%2)===1){uv.x=Math.ceil(uv.x)-uv.x}else{uv.x=uv.x-Math.floor(uv.x)}break}}if(uv.y<0||uv.y>1){switch(this.wrapT){case RepeatWrapping:uv.y=uv.y-Math.floor(uv.y);break;case ClampToEdgeWrapping:uv.y=uv.y<0?0:1;break;case MirroredRepeatWrapping:if(Math.abs(Math.floor(uv.y)%2)===1){uv.y=Math.ceil(uv.y)-uv.y}else{uv.y=uv.y-Math.floor(uv.y)}break}}if(this.flipY){uv.y=1-uv.y}return uv}set needsUpdate(value){if(value===true)this.version++}}Texture.DEFAULT_IMAGE=undefined;Texture.DEFAULT_MAPPING=UVMapping;Texture.prototype.isTexture=true;function serializeImage(image){if(typeof HTMLImageElement!=="undefined"&&image instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&image instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&image instanceof ImageBitmap){return ImageUtils.getDataURL(image)}else{if(image.data){return{data:Array.prototype.slice.call(image.data),width:image.width,height:image.height,type:image.data.constructor.name}}else{console.warn("THREE.Texture: Unable to serialize Texture.");return{}}}}class Vector4{constructor(x=0,y=0,z=0,w=1){this.x=x;this.y=y;this.z=z;this.w=w}get width(){return this.z}set width(value){this.z=value}get height(){return this.w}set height(value){this.w=value}set(x,y,z,w){this.x=x;this.y=y;this.z=z;this.w=w;return this}setScalar(scalar){this.x=scalar;this.y=scalar;this.z=scalar;this.w=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setZ(z){this.z=z;return this}setW(w){this.w=w;return this}setComponent(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}return this}getComponent(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(v){this.x=v.x;this.y=v.y;this.z=v.z;this.w=v.w!==undefined?v.w:1;return this}add(v,w){if(w!==undefined){console.warn("THREE.Vector4: .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;this.w+=v.w;return this}addScalar(s){this.x+=s;this.y+=s;this.z+=s;this.w+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;this.z+=v.z*s;this.w+=v.w*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.Vector4: .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;this.w-=v.w;return this}subScalar(s){this.x-=s;this.y-=s;this.z-=s;this.w-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this}multiply(v){this.x*=v.x;this.y*=v.y;this.z*=v.z;this.w*=v.w;return this}multiplyScalar(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;this.w*=scalar;return this}applyMatrix4(m){const x=this.x,y=this.y,z=this.z,w=this.w;const e=m.elements;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;this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}setAxisAngleFromQuaternion(q){this.w=2*Math.acos(q.w);const s=Math.sqrt(1-q.w*q.w);if(s<1e-4){this.x=1;this.y=0;this.z=0}else{this.x=q.x/s;this.y=q.y/s;this.z=q.z/s}return this}setAxisAngleFromRotationMatrix(m){let angle,x,y,z;const epsilon=.01,epsilon2=.1,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];if(Math.abs(m12-m21)yy&&xx>zz){if(xxzz){if(yy=0?1:-1,sqrSin=1-cos*cos;if(sqrSin>Number.EPSILON){const sin=Math.sqrt(sqrSin),len=Math.atan2(sin,cos*dir);s=Math.sin(s*len)/sin;t=Math.sin(t*len)/sin}const tDir=t*dir;x0=x0*s+x1*tDir;y0=y0*s+y1*tDir;z0=z0*s+z1*tDir;w0=w0*s+w1*tDir;if(s===1-t){const 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}static multiplyQuaternionsFlat(dst,dstOffset,src0,srcOffset0,src1,srcOffset1){const x0=src0[srcOffset0];const y0=src0[srcOffset0+1];const z0=src0[srcOffset0+2];const w0=src0[srcOffset0+3];const x1=src1[srcOffset1];const y1=src1[srcOffset1+1];const z1=src1[srcOffset1+2];const w1=src1[srcOffset1+3];dst[dstOffset]=x0*w1+w0*x1+y0*z1-z0*y1;dst[dstOffset+1]=y0*w1+w0*y1+z0*x1-x0*z1;dst[dstOffset+2]=z0*w1+w0*z1+x0*y1-y0*x1;dst[dstOffset+3]=w0*w1-x0*x1-y0*y1-z0*z1;return dst}get x(){return this._x}set x(value){this._x=value;this._onChangeCallback()}get y(){return this._y}set y(value){this._y=value;this._onChangeCallback()}get z(){return this._z}set z(value){this._z=value;this._onChangeCallback()}get w(){return this._w}set w(value){this._w=value;this._onChangeCallback()}set(x,y,z,w){this._x=x;this._y=y;this._z=z;this._w=w;this._onChangeCallback();return this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(quaternion){this._x=quaternion.x;this._y=quaternion.y;this._z=quaternion.z;this._w=quaternion.w;this._onChangeCallback();return this}setFromEuler(euler,update){if(!(euler&&euler.isEuler)){throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.")}const x=euler._x,y=euler._y,z=euler._z,order=euler._order;const cos=Math.cos;const sin=Math.sin;const c1=cos(x/2);const c2=cos(y/2);const c3=cos(z/2);const s1=sin(x/2);const s2=sin(y/2);const s3=sin(z/2);switch(order){case"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;break;case"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;break;case"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;break;case"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;break;case"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;break;case"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;break;default:console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: "+order)}if(update!==false)this._onChangeCallback();return this}setFromAxisAngle(axis,angle){const 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(m){const 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;if(trace>0){const s=.5/Math.sqrt(trace+1);this._w=.25/s;this._x=(m32-m23)*s;this._y=(m13-m31)*s;this._z=(m21-m12)*s}else if(m11>m22&&m11>m33){const s=2*Math.sqrt(1+m11-m22-m33);this._w=(m32-m23)/s;this._x=.25*s;this._y=(m12+m21)/s;this._z=(m13+m31)/s}else if(m22>m33){const s=2*Math.sqrt(1+m22-m11-m33);this._w=(m13-m31)/s;this._x=(m12+m21)/s;this._y=.25*s;this._z=(m23+m32)/s}else{const s=2*Math.sqrt(1+m33-m11-m22);this._w=(m21-m12)/s;this._x=(m13+m31)/s;this._y=(m23+m32)/s;this._z=.25*s}this._onChangeCallback();return this}setFromUnitVectors(vFrom,vTo){let r=vFrom.dot(vTo)+1;if(rMath.abs(vFrom.z)){this._x=-vFrom.y;this._y=vFrom.x;this._z=0;this._w=r}else{this._x=0;this._y=-vFrom.z;this._z=vFrom.y;this._w=r}}else{this._x=vFrom.y*vTo.z-vFrom.z*vTo.y;this._y=vFrom.z*vTo.x-vFrom.x*vTo.z;this._z=vFrom.x*vTo.y-vFrom.y*vTo.x;this._w=r}return this.normalize()}angleTo(q){return 2*Math.acos(Math.abs(clamp(this.dot(q),-1,1)))}rotateTowards(q,step){const angle=this.angleTo(q);if(angle===0)return this;const t=Math.min(1,step/angle);this.slerp(q,t);return this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){this._x*=-1;this._y*=-1;this._z*=-1;this._onChangeCallback();return this}dot(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let 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(q,p){if(p!==undefined){console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.");return this.multiplyQuaternions(q,p)}return this.multiplyQuaternions(this,q)}premultiply(q){return this.multiplyQuaternions(q,this)}multiplyQuaternions(a,b){const qax=a._x,qay=a._y,qaz=a._z,qaw=a._w;const 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(qb,t){if(t===0)return this;if(t===1)return this.copy(qb);const x=this._x,y=this._y,z=this._z,w=this._w;let 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){this._w=w;this._x=x;this._y=y;this._z=z;return this}const sqrSinHalfTheta=1-cosHalfTheta*cosHalfTheta;if(sqrSinHalfTheta<=Number.EPSILON){const s=1-t;this._w=s*w+t*this._w;this._x=s*x+t*this._x;this._y=s*y+t*this._y;this._z=s*z+t*this._z;this.normalize();this._onChangeCallback();return this}const sinHalfTheta=Math.sqrt(sqrSinHalfTheta);const halfTheta=Math.atan2(sinHalfTheta,cosHalfTheta);const 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}slerpQuaternions(qa,qb,t){this.copy(qa).slerp(qb,t)}equals(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w}fromArray(array,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(array=[],offset=0){array[offset]=this._x;array[offset+1]=this._y;array[offset+2]=this._z;array[offset+3]=this._w;return array}fromBufferAttribute(attribute,index){this._x=attribute.getX(index);this._y=attribute.getY(index);this._z=attribute.getZ(index);this._w=attribute.getW(index);return this}_onChange(callback){this._onChangeCallback=callback;return this}_onChangeCallback(){}}Quaternion.prototype.isQuaternion=true;class Vector3{constructor(x=0,y=0,z=0){this.x=x;this.y=y;this.z=z}set(x,y,z){if(z===undefined)z=this.z;this.x=x;this.y=y;this.z=z;return this}setScalar(scalar){this.x=scalar;this.y=scalar;this.z=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setZ(z){this.z=z;return this}setComponent(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(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(){return new this.constructor(this.x,this.y,this.z)}copy(v){this.x=v.x;this.y=v.y;this.z=v.z;return this}add(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x+=s;this.y+=s;this.z+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;this.z+=v.z*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x-=s;this.y-=s;this.z-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this}multiply(v,w){if(w!==undefined){console.warn("THREE.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(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;return this}multiplyVectors(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this}applyEuler(euler){if(!(euler&&euler.isEuler)){console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.")}return this.applyQuaternion(_quaternion$4.setFromEuler(euler))}applyAxisAngle(axis,angle){return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis,angle))}applyMatrix3(m){const x=this.x,y=this.y,z=this.z;const 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}applyNormalMatrix(m){return this.applyMatrix3(m).normalize()}applyMatrix4(m){const x=this.x,y=this.y,z=this.z;const e=m.elements;const 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(q){const x=this.x,y=this.y,z=this.z;const qx=q.x,qy=q.y,qz=q.z,qw=q.w;const ix=qw*x+qy*z-qz*y;const iy=qw*y+qz*x-qx*z;const iz=qw*z+qx*y-qy*x;const iw=-qx*x-qy*y-qz*z;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(camera){return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix)}unproject(camera){return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld)}transformDirection(m){const x=this.x,y=this.y,z=this.z;const 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(v){this.x/=v.x;this.y/=v.y;this.z/=v.z;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}min(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(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(min,max){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(minVal,maxVal){this.x=Math.max(minVal,Math.min(maxVal,this.x));this.y=Math.max(minVal,Math.min(maxVal,this.y));this.z=Math.max(minVal,Math.min(maxVal,this.z));return this}clampLength(min,max){const length=this.length();return this.divideScalar(length||1).multiplyScalar(Math.max(min,Math.min(max,length)))}floor(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this}ceil(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this}round(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this}roundToZero(){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(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this}dot(v){return this.x*v.x+this.y*v.y+this.z*v.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(length){return this.normalize().multiplyScalar(length)}lerp(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(v1,v2,alpha){this.x=v1.x+(v2.x-v1.x)*alpha;this.y=v1.y+(v2.y-v1.y)*alpha;this.z=v1.z+(v2.z-v1.z)*alpha;return this}cross(v,w){if(w!==undefined){console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.");return this.crossVectors(v,w)}return this.crossVectors(this,v)}crossVectors(a,b){const ax=a.x,ay=a.y,az=a.z;const 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(v){const denominator=v.lengthSq();if(denominator===0)return this.set(0,0,0);const scalar=v.dot(this)/denominator;return this.copy(v).multiplyScalar(scalar)}projectOnPlane(planeNormal){_vector$c.copy(this).projectOnVector(planeNormal);return this.sub(_vector$c)}reflect(normal){return this.sub(_vector$c.copy(normal).multiplyScalar(2*this.dot(normal)))}angleTo(v){const denominator=Math.sqrt(this.lengthSq()*v.lengthSq());if(denominator===0)return Math.PI/2;const theta=this.dot(v)/denominator;return Math.acos(clamp(theta,-1,1))}distanceTo(v){return Math.sqrt(this.distanceToSquared(v))}distanceToSquared(v){const dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz}manhattanDistanceTo(v){return Math.abs(this.x-v.x)+Math.abs(this.y-v.y)+Math.abs(this.z-v.z)}setFromSpherical(s){return this.setFromSphericalCoords(s.radius,s.phi,s.theta)}setFromSphericalCoords(radius,phi,theta){const sinPhiRadius=Math.sin(phi)*radius;this.x=sinPhiRadius*Math.sin(theta);this.y=Math.cos(phi)*radius;this.z=sinPhiRadius*Math.cos(theta);return this}setFromCylindrical(c){return this.setFromCylindricalCoords(c.radius,c.theta,c.y)}setFromCylindricalCoords(radius,theta,y){this.x=radius*Math.sin(theta);this.y=y;this.z=radius*Math.cos(theta);return this}setFromMatrixPosition(m){const e=m.elements;this.x=e[12];this.y=e[13];this.z=e[14];return this}setFromMatrixScale(m){const sx=this.setFromMatrixColumn(m,0).length();const sy=this.setFromMatrixColumn(m,1).length();const sz=this.setFromMatrixColumn(m,2).length();this.x=sx;this.y=sy;this.z=sz;return this}setFromMatrixColumn(m,index){return this.fromArray(m.elements,index*4)}setFromMatrix3Column(m,index){return this.fromArray(m.elements,index*3)}equals(v){return v.x===this.x&&v.y===this.y&&v.z===this.z}fromArray(array,offset=0){this.x=array[offset];this.y=array[offset+1];this.z=array[offset+2];return this}toArray(array=[],offset=0){array[offset]=this.x;array[offset+1]=this.y;array[offset+2]=this.z;return array}fromBufferAttribute(attribute,index,offset){if(offset!==undefined){console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute().")}this.x=attribute.getX(index);this.y=attribute.getY(index);this.z=attribute.getZ(index);return this}random(){this.x=Math.random();this.y=Math.random();this.z=Math.random();return this}}Vector3.prototype.isVector3=true;const _vector$c=new Vector3;const _quaternion$4=new Quaternion;class Box3{constructor(min=new Vector3(+Infinity,+Infinity,+Infinity),max=new Vector3(-Infinity,-Infinity,-Infinity)){this.min=min;this.max=max}set(min,max){this.min.copy(min);this.max.copy(max);return this}setFromArray(array){let minX=+Infinity;let minY=+Infinity;let minZ=+Infinity;let maxX=-Infinity;let maxY=-Infinity;let maxZ=-Infinity;for(let i=0,l=array.length;imaxX)maxX=x;if(y>maxY)maxY=y;if(z>maxZ)maxZ=z}this.min.set(minX,minY,minZ);this.max.set(maxX,maxY,maxZ);return this}setFromBufferAttribute(attribute){let minX=+Infinity;let minY=+Infinity;let minZ=+Infinity;let maxX=-Infinity;let maxY=-Infinity;let maxZ=-Infinity;for(let i=0,l=attribute.count;imaxX)maxX=x;if(y>maxY)maxY=y;if(z>maxZ)maxZ=z}this.min.set(minX,minY,minZ);this.max.set(maxX,maxY,maxZ);return this}setFromPoints(points){this.makeEmpty();for(let i=0,il=points.length;ithis.max.x||point.ythis.max.y||point.zthis.max.z?false:true}containsBox(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z}getParameter(point,target){if(target===undefined){console.warn("THREE.Box3: .getParameter() target is now required");target=new Vector3}return target.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(box){return box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z?false:true}intersectsSphere(sphere){this.clampPoint(sphere.center,_vector$b);return _vector$b.distanceToSquared(sphere.center)<=sphere.radius*sphere.radius}intersectsPlane(plane){let min,max;if(plane.normal.x>0){min=plane.normal.x*this.min.x;max=plane.normal.x*this.max.x}else{min=plane.normal.x*this.max.x;max=plane.normal.x*this.min.x}if(plane.normal.y>0){min+=plane.normal.y*this.min.y;max+=plane.normal.y*this.max.y}else{min+=plane.normal.y*this.max.y;max+=plane.normal.y*this.min.y}if(plane.normal.z>0){min+=plane.normal.z*this.min.z;max+=plane.normal.z*this.max.z}else{min+=plane.normal.z*this.max.z;max+=plane.normal.z*this.min.z}return min<=-plane.constant&&max>=-plane.constant}intersectsTriangle(triangle){if(this.isEmpty()){return false}this.getCenter(_center);_extents.subVectors(this.max,_center);_v0$2.subVectors(triangle.a,_center);_v1$7.subVectors(triangle.b,_center);_v2$3.subVectors(triangle.c,_center);_f0.subVectors(_v1$7,_v0$2);_f1.subVectors(_v2$3,_v1$7);_f2.subVectors(_v0$2,_v2$3);let axes=[0,-_f0.z,_f0.y,0,-_f1.z,_f1.y,0,-_f2.z,_f2.y,_f0.z,0,-_f0.x,_f1.z,0,-_f1.x,_f2.z,0,-_f2.x,-_f0.y,_f0.x,0,-_f1.y,_f1.x,0,-_f2.y,_f2.x,0];if(!satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)){return false}axes=[1,0,0,0,1,0,0,0,1];if(!satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)){return false}_triangleNormal.crossVectors(_f0,_f1);axes=[_triangleNormal.x,_triangleNormal.y,_triangleNormal.z];return satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)}clampPoint(point,target){if(target===undefined){console.warn("THREE.Box3: .clampPoint() target is now required");target=new Vector3}return target.copy(point).clamp(this.min,this.max)}distanceToPoint(point){const clampedPoint=_vector$b.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}getBoundingSphere(target){if(target===undefined){console.error("THREE.Box3: .getBoundingSphere() target is now required")}this.getCenter(target.center);target.radius=this.getSize(_vector$b).length()*.5;return target}intersect(box){this.min.max(box.min);this.max.min(box.max);if(this.isEmpty())this.makeEmpty();return this}union(box){this.min.min(box.min);this.max.max(box.max);return this}applyMatrix4(matrix){if(this.isEmpty())return this;_points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix);_points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix);_points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix);_points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix);_points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix);_points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix);_points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix);_points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix);this.setFromPoints(_points);return this}translate(offset){this.min.add(offset);this.max.add(offset);return this}equals(box){return box.min.equals(this.min)&&box.max.equals(this.max)}}Box3.prototype.isBox3=true;const _points=[new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3];const _vector$b=new Vector3;const _box$3=new Box3;const _v0$2=new Vector3;const _v1$7=new Vector3;const _v2$3=new Vector3;const _f0=new Vector3;const _f1=new Vector3;const _f2=new Vector3;const _center=new Vector3;const _extents=new Vector3;const _triangleNormal=new Vector3;const _testAxis=new Vector3;function satForAxes(axes,v0,v1,v2,extents){for(let i=0,j=axes.length-3;i<=j;i+=3){_testAxis.fromArray(axes,i);const r=extents.x*Math.abs(_testAxis.x)+extents.y*Math.abs(_testAxis.y)+extents.z*Math.abs(_testAxis.z);const p0=v0.dot(_testAxis);const p1=v1.dot(_testAxis);const p2=v2.dot(_testAxis);if(Math.max(-Math.max(p0,p1,p2),Math.min(p0,p1,p2))>r){return false}}return true}const _box$2=new Box3;const _v1$6=new Vector3;const _toFarthestPoint=new Vector3;const _toPoint=new Vector3;class Sphere{constructor(center=new Vector3,radius=-1){this.center=center;this.radius=radius}set(center,radius){this.center.copy(center);this.radius=radius;return this}setFromPoints(points,optionalCenter){const center=this.center;if(optionalCenter!==undefined){center.copy(optionalCenter)}else{_box$2.setFromPoints(points).getCenter(center)}let maxRadiusSq=0;for(let i=0,il=points.length;ithis.radius*this.radius){target.sub(this.center).normalize();target.multiplyScalar(this.radius).add(this.center)}return target}getBoundingBox(target){if(target===undefined){console.warn("THREE.Sphere: .getBoundingBox() target is now required");target=new Box3}if(this.isEmpty()){target.makeEmpty();return target}target.set(this.center,this.center);target.expandByScalar(this.radius);return target}applyMatrix4(matrix){this.center.applyMatrix4(matrix);this.radius=this.radius*matrix.getMaxScaleOnAxis();return this}translate(offset){this.center.add(offset);return this}expandByPoint(point){_toPoint.subVectors(point,this.center);const lengthSq=_toPoint.lengthSq();if(lengthSq>this.radius*this.radius){const length=Math.sqrt(lengthSq);const missingRadiusHalf=(length-this.radius)*.5;this.center.add(_toPoint.multiplyScalar(missingRadiusHalf/length));this.radius+=missingRadiusHalf}return this}union(sphere){_toFarthestPoint.subVectors(sphere.center,this.center).normalize().multiplyScalar(sphere.radius);this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint));this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint));return this}equals(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}clone(){return(new this.constructor).copy(this)}}const _vector$a=new Vector3;const _segCenter=new Vector3;const _segDir=new Vector3;const _diff=new Vector3;const _edge1=new Vector3;const _edge2=new Vector3;const _normal$1=new Vector3;class Ray{constructor(origin=new Vector3,direction=new Vector3(0,0,-1)){this.origin=origin;this.direction=direction}set(origin,direction){this.origin.copy(origin);this.direction.copy(direction);return this}copy(ray){this.origin.copy(ray.origin);this.direction.copy(ray.direction);return this}at(t,target){if(target===undefined){console.warn("THREE.Ray: .at() target is now required");target=new Vector3}return target.copy(this.direction).multiplyScalar(t).add(this.origin)}lookAt(v){this.direction.copy(v).sub(this.origin).normalize();return this}recast(t){this.origin.copy(this.at(t,_vector$a));return this}closestPointToPoint(point,target){if(target===undefined){console.warn("THREE.Ray: .closestPointToPoint() target is now required");target=new Vector3}target.subVectors(point,this.origin);const directionDistance=target.dot(this.direction);if(directionDistance<0){return target.copy(this.origin)}return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)}distanceToPoint(point){return Math.sqrt(this.distanceSqToPoint(point))}distanceSqToPoint(point){const directionDistance=_vector$a.subVectors(point,this.origin).dot(this.direction);if(directionDistance<0){return this.origin.distanceToSquared(point)}_vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);return _vector$a.distanceToSquared(point)}distanceSqToSegment(v0,v1,optionalPointOnRay,optionalPointOnSegment){_segCenter.copy(v0).add(v1).multiplyScalar(.5);_segDir.copy(v1).sub(v0).normalize();_diff.copy(this.origin).sub(_segCenter);const segExtent=v0.distanceTo(v1)*.5;const a01=-this.direction.dot(_segDir);const b0=_diff.dot(this.direction);const b1=-_diff.dot(_segDir);const c=_diff.lengthSq();const det=Math.abs(1-a01*a01);let s0,s1,sqrDist,extDet;if(det>0){s0=a01*b1-b0;s1=a01*b0-b1;extDet=segExtent*det;if(s0>=0){if(s1>=-extDet){if(s1<=extDet){const invDet=1/det;s0*=invDet;s1*=invDet;sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else{s1=segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}}else{s1=-segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}}else{if(s1<=-extDet){s0=Math.max(0,-(-a01*segExtent+b0));s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=-s0*s0+s1*(s1+2*b1)+c}else if(s1<=extDet){s0=0;s1=Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=s1*(s1+2*b1)+c}else{s0=Math.max(0,-(a01*segExtent+b0));s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=-s0*s0+s1*(s1+2*b1)+c}}}else{s1=a01>0?-segExtent:segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}if(optionalPointOnRay){optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin)}if(optionalPointOnSegment){optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter)}return sqrDist}intersectSphere(sphere,target){_vector$a.subVectors(sphere.center,this.origin);const tca=_vector$a.dot(this.direction);const d2=_vector$a.dot(_vector$a)-tca*tca;const radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;const thc=Math.sqrt(radius2-d2);const t0=tca-thc;const t1=tca+thc;if(t0<0&&t1<0)return null;if(t0<0)return this.at(t1,target);return this.at(t0,target)}intersectsSphere(sphere){return this.distanceSqToPoint(sphere.center)<=sphere.radius*sphere.radius}distanceToPlane(plane){const denominator=plane.normal.dot(this.direction);if(denominator===0){if(plane.distanceToPoint(this.origin)===0){return 0}return null}const t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null}intersectPlane(plane,target){const t=this.distanceToPlane(plane);if(t===null){return null}return this.at(t,target)}intersectsPlane(plane){const distToPoint=plane.distanceToPoint(this.origin);if(distToPoint===0){return true}const denominator=plane.normal.dot(this.direction);if(denominator*distToPoint<0){return true}return false}intersectBox(box,target){let tmin,tmax,tymin,tymax,tzmin,tzmax;const invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z;const origin=this.origin;if(invdirx>=0){tmin=(box.min.x-origin.x)*invdirx;tmax=(box.max.x-origin.x)*invdirx}else{tmin=(box.max.x-origin.x)*invdirx;tmax=(box.min.x-origin.x)*invdirx}if(invdiry>=0){tymin=(box.min.y-origin.y)*invdiry;tymax=(box.max.y-origin.y)*invdiry}else{tymin=(box.max.y-origin.y)*invdiry;tymax=(box.min.y-origin.y)*invdiry}if(tmin>tymax||tymin>tmax)return null;if(tymin>tmin||tmin!==tmin)tmin=tymin;if(tymax=0){tzmin=(box.min.z-origin.z)*invdirz;tzmax=(box.max.z-origin.z)*invdirz}else{tzmin=(box.max.z-origin.z)*invdirz;tzmax=(box.min.z-origin.z)*invdirz}if(tmin>tzmax||tzmin>tmax)return null;if(tzmin>tmin||tmin!==tmin)tmin=tzmin;if(tzmax=0?tmin:tmax,target)}intersectsBox(box){return this.intersectBox(box,_vector$a)!==null}intersectTriangle(a,b,c,backfaceCulling,target){_edge1.subVectors(b,a);_edge2.subVectors(c,a);_normal$1.crossVectors(_edge1,_edge2);let DdN=this.direction.dot(_normal$1);let sign;if(DdN>0){if(backfaceCulling)return null;sign=1}else if(DdN<0){sign=-1;DdN=-DdN}else{return null}_diff.subVectors(this.origin,a);const DdQxE2=sign*this.direction.dot(_edge2.crossVectors(_diff,_edge2));if(DdQxE2<0){return null}const DdE1xQ=sign*this.direction.dot(_edge1.cross(_diff));if(DdE1xQ<0){return null}if(DdQxE2+DdE1xQ>DdN){return null}const QdN=-sign*_diff.dot(_normal$1);if(QdN<0){return null}return this.at(QdN/DdN,target)}applyMatrix4(matrix4){this.origin.applyMatrix4(matrix4);this.direction.transformDirection(matrix4);return this}equals(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}clone(){return(new this.constructor).copy(this)}}class Matrix4{constructor(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];if(arguments.length>0){console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")}}set(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){const 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(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this}clone(){return(new Matrix4).fromArray(this.elements)}copy(m){const te=this.elements;const 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(m){const te=this.elements,me=m.elements;te[12]=me[12];te[13]=me[13];te[14]=me[14];return this}setFromMatrix3(m){const me=m.elements;this.set(me[0],me[3],me[6],0,me[1],me[4],me[7],0,me[2],me[5],me[8],0,0,0,0,1);return this}extractBasis(xAxis,yAxis,zAxis){xAxis.setFromMatrixColumn(this,0);yAxis.setFromMatrixColumn(this,1);zAxis.setFromMatrixColumn(this,2);return this}makeBasis(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(m){const te=this.elements;const me=m.elements;const scaleX=1/_v1$5.setFromMatrixColumn(m,0).length();const scaleY=1/_v1$5.setFromMatrixColumn(m,1).length();const scaleZ=1/_v1$5.setFromMatrixColumn(m,2).length();te[0]=me[0]*scaleX;te[1]=me[1]*scaleX;te[2]=me[2]*scaleX;te[3]=0;te[4]=me[4]*scaleY;te[5]=me[5]*scaleY;te[6]=me[6]*scaleY;te[7]=0;te[8]=me[8]*scaleZ;te[9]=me[9]*scaleZ;te[10]=me[10]*scaleZ;te[11]=0;te[12]=0;te[13]=0;te[14]=0;te[15]=1;return this}makeRotationFromEuler(euler){if(!(euler&&euler.isEuler)){console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.")}const te=this.elements;const x=euler.x,y=euler.y,z=euler.z;const a=Math.cos(x),b=Math.sin(x);const c=Math.cos(y),d=Math.sin(y);const e=Math.cos(z),f=Math.sin(z);if(euler.order==="XYZ"){const 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"){const 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"){const 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"){const 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"){const 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"){const 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}te[3]=0;te[7]=0;te[11]=0;te[12]=0;te[13]=0;te[14]=0;te[15]=1;return this}makeRotationFromQuaternion(q){return this.compose(_zero,q,_one)}lookAt(eye,target,up){const te=this.elements;_z.subVectors(eye,target);if(_z.lengthSq()===0){_z.z=1}_z.normalize();_x.crossVectors(up,_z);if(_x.lengthSq()===0){if(Math.abs(up.z)===1){_z.x+=1e-4}else{_z.z+=1e-4}_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(m,n){if(n!==undefined){console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.");return this.multiplyMatrices(m,n)}return this.multiplyMatrices(this,m)}premultiply(m){return this.multiplyMatrices(m,this)}multiplyMatrices(a,b){const ae=a.elements;const be=b.elements;const te=this.elements;const a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12];const a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13];const a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14];const a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15];const b11=be[0],b12=be[4],b13=be[8],b14=be[12];const b21=be[1],b22=be[5],b23=be[9],b24=be[13];const b31=be[2],b32=be[6],b33=be[10],b34=be[14];const 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(s){const 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}determinant(){const te=this.elements;const n11=te[0],n12=te[4],n13=te[8],n14=te[12];const n21=te[1],n22=te[5],n23=te[9],n24=te[13];const n31=te[2],n32=te[6],n33=te[10],n34=te[14];const n41=te[3],n42=te[7],n43=te[11],n44=te[15];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(){const te=this.elements;let 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(x,y,z){const te=this.elements;if(x.isVector3){te[12]=x.x;te[13]=x.y;te[14]=x.z}else{te[12]=x;te[13]=y;te[14]=z}return this}invert(){const te=this.elements,n11=te[0],n21=te[1],n31=te[2],n41=te[3],n12=te[4],n22=te[5],n32=te[6],n42=te[7],n13=te[8],n23=te[9],n33=te[10],n43=te[11],n14=te[12],n24=te[13],n34=te[14],n44=te[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;const det=n11*t11+n21*t12+n31*t13+n41*t14;if(det===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const 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(v){const te=this.elements;const 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(){const te=this.elements;const scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2];const scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6];const scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))}makeTranslation(x,y,z){this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1);return this}makeRotationX(theta){const 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(theta){const 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(theta){const 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(axis,angle){const c=Math.cos(angle);const s=Math.sin(angle);const t=1-c;const x=axis.x,y=axis.y,z=axis.z;const 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(x,y,z){this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1);return this}makeShear(x,y,z){this.set(1,y,z,0,x,1,z,0,x,y,1,0,0,0,0,1);return this}compose(position,quaternion,scale){const te=this.elements;const x=quaternion._x,y=quaternion._y,z=quaternion._z,w=quaternion._w;const x2=x+x,y2=y+y,z2=z+z;const xx=x*x2,xy=x*y2,xz=x*z2;const yy=y*y2,yz=y*z2,zz=z*z2;const wx=w*x2,wy=w*y2,wz=w*z2;const sx=scale.x,sy=scale.y,sz=scale.z;te[0]=(1-(yy+zz))*sx;te[1]=(xy+wz)*sx;te[2]=(xz-wy)*sx;te[3]=0;te[4]=(xy-wz)*sy;te[5]=(1-(xx+zz))*sy;te[6]=(yz+wx)*sy;te[7]=0;te[8]=(xz+wy)*sz;te[9]=(yz-wx)*sz;te[10]=(1-(xx+yy))*sz;te[11]=0;te[12]=position.x;te[13]=position.y;te[14]=position.z;te[15]=1;return this}decompose(position,quaternion,scale){const te=this.elements;let sx=_v1$5.set(te[0],te[1],te[2]).length();const sy=_v1$5.set(te[4],te[5],te[6]).length();const sz=_v1$5.set(te[8],te[9],te[10]).length();const det=this.determinant();if(det<0)sx=-sx;position.x=te[12];position.y=te[13];position.z=te[14];_m1$2.copy(this);const invSX=1/sx;const invSY=1/sy;const invSZ=1/sz;_m1$2.elements[0]*=invSX;_m1$2.elements[1]*=invSX;_m1$2.elements[2]*=invSX;_m1$2.elements[4]*=invSY;_m1$2.elements[5]*=invSY;_m1$2.elements[6]*=invSY;_m1$2.elements[8]*=invSZ;_m1$2.elements[9]*=invSZ;_m1$2.elements[10]*=invSZ;quaternion.setFromRotationMatrix(_m1$2);scale.x=sx;scale.y=sy;scale.z=sz;return this}makePerspective(left,right,top,bottom,near,far){if(far===undefined){console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.")}const te=this.elements;const x=2*near/(right-left);const y=2*near/(top-bottom);const a=(right+left)/(right-left);const b=(top+bottom)/(top-bottom);const c=-(far+near)/(far-near);const 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(left,right,top,bottom,near,far){const te=this.elements;const w=1/(right-left);const h=1/(top-bottom);const p=1/(far-near);const x=(right+left)*w;const y=(top+bottom)*h;const 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(matrix){const te=this.elements;const me=matrix.elements;for(let i=0;i<16;i++){if(te[i]!==me[i])return false}return true}fromArray(array,offset=0){for(let i=0;i<16;i++){this.elements[i]=array[i+offset]}return this}toArray(array=[],offset=0){const 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}}Matrix4.prototype.isMatrix4=true;const _v1$5=new Vector3;const _m1$2=new Matrix4;const _zero=new Vector3(0,0,0);const _one=new Vector3(1,1,1);const _x=new Vector3;const _y=new Vector3;const _z=new Vector3;const _matrix$1=new Matrix4;const _quaternion$3=new Quaternion;class Euler{constructor(x=0,y=0,z=0,order=Euler.DefaultOrder){this._x=x;this._y=y;this._z=z;this._order=order}get x(){return this._x}set x(value){this._x=value;this._onChangeCallback()}get y(){return this._y}set y(value){this._y=value;this._onChangeCallback()}get z(){return this._z}set z(value){this._z=value;this._onChangeCallback()}get order(){return this._order}set order(value){this._order=value;this._onChangeCallback()}set(x,y,z,order){this._x=x;this._y=y;this._z=z;this._order=order||this._order;this._onChangeCallback();return this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(euler){this._x=euler._x;this._y=euler._y;this._z=euler._z;this._order=euler._order;this._onChangeCallback();return this}setFromRotationMatrix(m,order,update){const te=m.elements;const m11=te[0],m12=te[4],m13=te[8];const m21=te[1],m22=te[5],m23=te[9];const m31=te[2],m32=te[6],m33=te[10];order=order||this._order;switch(order){case"XYZ":this._y=Math.asin(clamp(m13,-1,1));if(Math.abs(m13)<.9999999){this._x=Math.atan2(-m23,m33);this._z=Math.atan2(-m12,m11)}else{this._x=Math.atan2(m32,m22);this._z=0}break;case"YXZ":this._x=Math.asin(-clamp(m23,-1,1));if(Math.abs(m23)<.9999999){this._y=Math.atan2(m13,m33);this._z=Math.atan2(m21,m22)}else{this._y=Math.atan2(-m31,m11);this._z=0}break;case"ZXY":this._x=Math.asin(clamp(m32,-1,1));if(Math.abs(m32)<.9999999){this._y=Math.atan2(-m31,m33);this._z=Math.atan2(-m12,m22)}else{this._y=0;this._z=Math.atan2(m21,m11)}break;case"ZYX":this._y=Math.asin(-clamp(m31,-1,1));if(Math.abs(m31)<.9999999){this._x=Math.atan2(m32,m33);this._z=Math.atan2(m21,m11)}else{this._x=0;this._z=Math.atan2(-m12,m22)}break;case"YZX":this._z=Math.asin(clamp(m21,-1,1));if(Math.abs(m21)<.9999999){this._x=Math.atan2(-m23,m22);this._y=Math.atan2(-m31,m11)}else{this._x=0;this._y=Math.atan2(m13,m33)}break;case"XZY":this._z=Math.asin(-clamp(m12,-1,1));if(Math.abs(m12)<.9999999){this._x=Math.atan2(m32,m22);this._y=Math.atan2(m13,m11)}else{this._x=Math.atan2(-m23,m33);this._y=0}break;default:console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: "+order)}this._order=order;if(update!==false)this._onChangeCallback();return this}setFromQuaternion(q,order,update){_matrix$1.makeRotationFromQuaternion(q);return this.setFromRotationMatrix(_matrix$1,order,update)}setFromVector3(v,order){return this.set(v.x,v.y,v.z,order||this._order)}reorder(newOrder){_quaternion$3.setFromEuler(this);return this.setFromQuaternion(_quaternion$3,newOrder)}equals(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order}fromArray(array){this._x=array[0];this._y=array[1];this._z=array[2];if(array[3]!==undefined)this._order=array[3];this._onChangeCallback();return this}toArray(array=[],offset=0){array[offset]=this._x;array[offset+1]=this._y;array[offset+2]=this._z;array[offset+3]=this._order;return array}toVector3(optionalResult){if(optionalResult){return optionalResult.set(this._x,this._y,this._z)}else{return new Vector3(this._x,this._y,this._z)}}_onChange(callback){this._onChangeCallback=callback;return this}_onChangeCallback(){}}Euler.prototype.isEuler=true;Euler.DefaultOrder="XYZ";Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];class Layers{constructor(){this.mask=1|0}set(channel){this.mask=1<1){for(let i=0;i1){for(let i=0;i0){object.children=[];for(let i=0;i0){object.animations=[];for(let i=0;i0)output.geometries=geometries;if(materials.length>0)output.materials=materials;if(textures.length>0)output.textures=textures;if(images.length>0)output.images=images;if(shapes.length>0)output.shapes=shapes;if(skeletons.length>0)output.skeletons=skeletons;if(animations.length>0)output.animations=animations}output.object=object;return output;function extractFromCache(cache){const values=[];for(const key in cache){const data=cache[key];delete data.metadata;values.push(data)}return values}}clone(recursive){return(new this.constructor).copy(this,recursive)}copy(source,recursive=true){this.name=source.name;this.up.copy(source.up);this.position.copy(source.position);this.rotation.order=source.rotation.order;this.quaternion.copy(source.quaternion);this.scale.copy(source.scale);this.matrix.copy(source.matrix);this.matrixWorld.copy(source.matrixWorld);this.matrixAutoUpdate=source.matrixAutoUpdate;this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate;this.layers.mask=source.layers.mask;this.visible=source.visible;this.castShadow=source.castShadow;this.receiveShadow=source.receiveShadow;this.frustumCulled=source.frustumCulled;this.renderOrder=source.renderOrder;this.userData=JSON.parse(JSON.stringify(source.userData));if(recursive===true){for(let i=0;i1){return null}return target.copy(direction).multiplyScalar(t).add(line.start)}intersectsLine(line){const startSign=this.distanceToPoint(line.start);const endSign=this.distanceToPoint(line.end);return startSign<0&&endSign>0||endSign<0&&startSign>0}intersectsBox(box){return box.intersectsPlane(this)}intersectsSphere(sphere){return sphere.intersectsPlane(this)}coplanarPoint(target){if(target===undefined){console.warn("THREE.Plane: .coplanarPoint() target is now required");target=new Vector3}return target.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(matrix,optionalNormalMatrix){const normalMatrix=optionalNormalMatrix||_normalMatrix.getNormalMatrix(matrix);const referencePoint=this.coplanarPoint(_vector1).applyMatrix4(matrix);const normal=this.normal.applyMatrix3(normalMatrix).normalize();this.constant=-referencePoint.dot(normal);return this}translate(offset){this.constant-=offset.dot(this.normal);return this}equals(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}clone(){return(new this.constructor).copy(this)}}Plane.prototype.isPlane=true;const _v0$1=new Vector3;const _v1$3=new Vector3;const _v2$2=new Vector3;const _v3$1=new Vector3;const _vab=new Vector3;const _vac=new Vector3;const _vbc=new Vector3;const _vap=new Vector3;const _vbp=new Vector3;const _vcp=new Vector3;class Triangle{constructor(a=new Vector3,b=new Vector3,c=new Vector3){this.a=a;this.b=b;this.c=c}static getNormal(a,b,c,target){if(target===undefined){console.warn("THREE.Triangle: .getNormal() target is now required");target=new Vector3}target.subVectors(c,b);_v0$1.subVectors(a,b);target.cross(_v0$1);const targetLengthSq=target.lengthSq();if(targetLengthSq>0){return target.multiplyScalar(1/Math.sqrt(targetLengthSq))}return target.set(0,0,0)}static getBarycoord(point,a,b,c,target){_v0$1.subVectors(c,a);_v1$3.subVectors(b,a);_v2$2.subVectors(point,a);const dot00=_v0$1.dot(_v0$1);const dot01=_v0$1.dot(_v1$3);const dot02=_v0$1.dot(_v2$2);const dot11=_v1$3.dot(_v1$3);const dot12=_v1$3.dot(_v2$2);const denom=dot00*dot11-dot01*dot01;if(target===undefined){console.warn("THREE.Triangle: .getBarycoord() target is now required");target=new Vector3}if(denom===0){return target.set(-2,-1,-1)}const invDenom=1/denom;const u=(dot11*dot02-dot01*dot12)*invDenom;const v=(dot00*dot12-dot01*dot02)*invDenom;return target.set(1-u-v,v,u)}static containsPoint(point,a,b,c){this.getBarycoord(point,a,b,c,_v3$1);return _v3$1.x>=0&&_v3$1.y>=0&&_v3$1.x+_v3$1.y<=1}static getUV(point,p1,p2,p3,uv1,uv2,uv3,target){this.getBarycoord(point,p1,p2,p3,_v3$1);target.set(0,0);target.addScaledVector(uv1,_v3$1.x);target.addScaledVector(uv2,_v3$1.y);target.addScaledVector(uv3,_v3$1.z);return target}static isFrontFacing(a,b,c,direction){_v0$1.subVectors(c,b);_v1$3.subVectors(a,b);return _v0$1.cross(_v1$3).dot(direction)<0?true:false}set(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this}setFromPointsAndIndices(points,i0,i1,i2){this.a.copy(points[i0]);this.b.copy(points[i1]);this.c.copy(points[i2]);return this}clone(){return(new this.constructor).copy(this)}copy(triangle){this.a.copy(triangle.a);this.b.copy(triangle.b);this.c.copy(triangle.c);return this}getArea(){_v0$1.subVectors(this.c,this.b);_v1$3.subVectors(this.a,this.b);return _v0$1.cross(_v1$3).length()*.5}getMidpoint(target){if(target===undefined){console.warn("THREE.Triangle: .getMidpoint() target is now required");target=new Vector3}return target.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(target){return Triangle.getNormal(this.a,this.b,this.c,target)}getPlane(target){if(target===undefined){console.warn("THREE.Triangle: .getPlane() target is now required");target=new Plane}return target.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(point,target){return Triangle.getBarycoord(point,this.a,this.b,this.c,target)}getUV(point,uv1,uv2,uv3,target){return Triangle.getUV(point,this.a,this.b,this.c,uv1,uv2,uv3,target)}containsPoint(point){return Triangle.containsPoint(point,this.a,this.b,this.c)}isFrontFacing(direction){return Triangle.isFrontFacing(this.a,this.b,this.c,direction)}intersectsBox(box){return box.intersectsTriangle(this)}closestPointToPoint(p,target){if(target===undefined){console.warn("THREE.Triangle: .closestPointToPoint() target is now required");target=new Vector3}const a=this.a,b=this.b,c=this.c;let v,w;_vab.subVectors(b,a);_vac.subVectors(c,a);_vap.subVectors(p,a);const d1=_vab.dot(_vap);const d2=_vac.dot(_vap);if(d1<=0&&d2<=0){return target.copy(a)}_vbp.subVectors(p,b);const d3=_vab.dot(_vbp);const d4=_vac.dot(_vbp);if(d3>=0&&d4<=d3){return target.copy(b)}const vc=d1*d4-d3*d2;if(vc<=0&&d1>=0&&d3<=0){v=d1/(d1-d3);return target.copy(a).addScaledVector(_vab,v)}_vcp.subVectors(p,c);const d5=_vab.dot(_vcp);const d6=_vac.dot(_vcp);if(d6>=0&&d5<=d6){return target.copy(c)}const vb=d5*d2-d1*d6;if(vb<=0&&d2>=0&&d6<=0){w=d2/(d2-d6);return target.copy(a).addScaledVector(_vac,w)}const va=d3*d6-d5*d4;if(va<=0&&d4-d3>=0&&d5-d6>=0){_vbc.subVectors(c,b);w=(d4-d3)/(d4-d3+(d5-d6));return target.copy(b).addScaledVector(_vbc,w)}const denom=1/(va+vb+vc);v=vb*denom;w=vc*denom;return target.copy(a).addScaledVector(_vab,v).addScaledVector(_vac,w)}equals(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}}let materialId=0;function Material(){Object.defineProperty(this,"id",{value:materialId++});this.uuid=generateUUID();this.name="";this.type="Material";this.fog=true;this.blending=NormalBlending;this.side=FrontSide;this.vertexColors=false;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.stencilWriteMask=255;this.stencilFunc=AlwaysStencilFunc;this.stencilRef=0;this.stencilFuncMask=255;this.stencilFail=KeepStencilOp;this.stencilZFail=KeepStencilOp;this.stencilZPass=KeepStencilOp;this.stencilWrite=false;this.clippingPlanes=null;this.clipIntersection=false;this.clipShadows=false;this.shadowSide=null;this.colorWrite=true;this.precision=null;this.polygonOffset=false;this.polygonOffsetFactor=0;this.polygonOffsetUnits=0;this.dithering=false;this.alphaTest=0;this.alphaToCoverage=false;this.premultipliedAlpha=false;this.visible=true;this.toneMapped=true;this.userData={};this.version=0}Material.prototype=Object.assign(Object.create(EventDispatcher$1.prototype),{constructor:Material,isMaterial:true,onBuild:function(){},onBeforeCompile:function(){},customProgramCacheKey:function(){return this.onBeforeCompile.toString()},setValues:function(values){if(values===undefined)return;for(const key in values){const newValue=values[key];if(newValue===undefined){console.warn("THREE.Material: '"+key+"' parameter is undefined.");continue}if(key==="shading"){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.");this.flatShading=newValue===FlatShading?true:false;continue}const currentValue=this[key];if(currentValue===undefined){console.warn("THREE."+this.type+": '"+key+"' is not a property of this material.");continue}if(currentValue&¤tValue.isColor){currentValue.set(newValue)}else if(currentValue&¤tValue.isVector3&&(newValue&&newValue.isVector3)){currentValue.copy(newValue)}else{this[key]=newValue}}},toJSON:function(meta){const isRoot=meta===undefined||typeof meta==="string";if(isRoot){meta={textures:{},images:{}}}const data={metadata:{version:4.5,type:"Material",generator:"Material.toJSON"}};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.sheen&&this.sheen.isColor)data.sheen=this.sheen.getHex();if(this.emissive&&this.emissive.isColor)data.emissive=this.emissive.getHex();if(this.emissiveIntensity&&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.clearcoatMap&&this.clearcoatMap.isTexture){data.clearcoatMap=this.clearcoatMap.toJSON(meta).uuid}if(this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture){data.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(meta).uuid}if(this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture){data.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(meta).uuid;data.clearcoatNormalScale=this.clearcoatNormalScale.toArray()}if(this.map&&this.map.isTexture)data.map=this.map.toJSON(meta).uuid;if(this.matcap&&this.matcap.isTexture)data.matcap=this.matcap.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;data.lightMapIntensity=this.lightMapIntensity}if(this.aoMap&&this.aoMap.isTexture){data.aoMap=this.aoMap.toJSON(meta).uuid;data.aoMapIntensity=this.aoMapIntensity}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.normalMapType=this.normalMapType;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;if(this.combine!==undefined)data.combine=this.combine}if(this.envMapIntensity!==undefined)data.envMapIntensity=this.envMapIntensity;if(this.reflectivity!==undefined)data.reflectivity=this.reflectivity;if(this.refractionRatio!==undefined)data.refractionRatio=this.refractionRatio;if(this.gradientMap&&this.gradientMap.isTexture){data.gradientMap=this.gradientMap.toJSON(meta).uuid}if(this.size!==undefined)data.size=this.size;if(this.shadowSide!==null)data.shadowSide=this.shadowSide;if(this.sizeAttenuation!==undefined)data.sizeAttenuation=this.sizeAttenuation;if(this.blending!==NormalBlending)data.blending=this.blending;if(this.side!==FrontSide)data.side=this.side;if(this.vertexColors)data.vertexColors=true;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;data.colorWrite=this.colorWrite;data.stencilWrite=this.stencilWrite;data.stencilWriteMask=this.stencilWriteMask;data.stencilFunc=this.stencilFunc;data.stencilRef=this.stencilRef;data.stencilFuncMask=this.stencilFuncMask;data.stencilFail=this.stencilFail;data.stencilZFail=this.stencilZFail;data.stencilZPass=this.stencilZPass;if(this.rotation&&this.rotation!==0)data.rotation=this.rotation;if(this.polygonOffset===true)data.polygonOffset=true;if(this.polygonOffsetFactor!==0)data.polygonOffsetFactor=this.polygonOffsetFactor;if(this.polygonOffsetUnits!==0)data.polygonOffsetUnits=this.polygonOffsetUnits;if(this.linewidth&&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.alphaToCoverage===true)data.alphaToCoverage=this.alphaToCoverage;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.morphNormals===true)data.morphNormals=true;if(this.skinning===true)data.skinning=true;if(this.flatShading===true)data.flatShading=this.flatShading;if(this.visible===false)data.visible=false;if(this.toneMapped===false)data.toneMapped=false;if(JSON.stringify(this.userData)!=="{}")data.userData=this.userData;function extractFromCache(cache){const values=[];for(const key in cache){const data=cache[key];delete data.metadata;values.push(data)}return values}if(isRoot){const textures=extractFromCache(meta.textures);const 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.blending=source.blending;this.side=source.side;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.stencilWriteMask=source.stencilWriteMask;this.stencilFunc=source.stencilFunc;this.stencilRef=source.stencilRef;this.stencilFuncMask=source.stencilFuncMask;this.stencilFail=source.stencilFail;this.stencilZFail=source.stencilZFail;this.stencilZPass=source.stencilZPass;this.stencilWrite=source.stencilWrite;const srcPlanes=source.clippingPlanes;let dstPlanes=null;if(srcPlanes!==null){const n=srcPlanes.length;dstPlanes=new Array(n);for(let i=0;i!==n;++i){dstPlanes[i]=srcPlanes[i].clone()}}this.clippingPlanes=dstPlanes;this.clipIntersection=source.clipIntersection;this.clipShadows=source.clipShadows;this.shadowSide=source.shadowSide;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.alphaToCoverage=source.alphaToCoverage;this.premultipliedAlpha=source.premultipliedAlpha;this.visible=source.visible;this.toneMapped=source.toneMapped;this.userData=JSON.parse(JSON.stringify(source.userData));return this},dispose:function(){this.dispatchEvent({type:"dispose"})}});Object.defineProperty(Material.prototype,"needsUpdate",{set:function(value){if(value===true)this.version++}});const _colorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};const _hslA={h:0,s:0,l:0};const _hslB={h:0,s:0,l:0};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}function SRGBToLinear(c){return c<.04045?c*.0773993808:Math.pow(c*.9478672986+.0521327014,2.4)}function LinearToSRGB(c){return c<.0031308?c*12.92:1.055*Math.pow(c,.41666)-.055}class Color{constructor(r,g,b){if(g===undefined&&b===undefined){return this.set(r)}return this.setRGB(r,g,b)}set(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(scalar){this.r=scalar;this.g=scalar;this.b=scalar;return this}setHex(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(r,g,b){this.r=r;this.g=g;this.b=b;return this}setHSL(h,s,l){h=euclideanModulo(h,1);s=clamp(s,0,1);l=clamp(l,0,1);if(s===0){this.r=this.g=this.b=l}else{const p=l<=.5?l*(1+s):l+s-l*s;const 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(style){function handleAlpha(string){if(string===undefined)return;if(parseFloat(string)<1){console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}}let m;if(m=/^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(style)){let color;const name=m[1];const components=m[2];switch(name){case"rgb":case"rgba":if(color=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){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[4]);return this}if(color=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){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[4]);return this}break;case"hsl":case"hsla":if(color=/^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){const h=parseFloat(color[1])/360;const s=parseInt(color[2],10)/100;const l=parseInt(color[3],10)/100;handleAlpha(color[4]);return this.setHSL(h,s,l)}break}}else if(m=/^\#([A-Fa-f\d]+)$/.exec(style)){const hex=m[1];const size=hex.length;if(size===3){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){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){return this.setColorName(style)}return this}setColorName(style){const hex=_colorKeywords[style.toLowerCase()];if(hex!==undefined){this.setHex(hex)}else{console.warn("THREE.Color: Unknown color "+style)}return this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(color){this.r=color.r;this.g=color.g;this.b=color.b;return this}copyGammaToLinear(color,gammaFactor=2){this.r=Math.pow(color.r,gammaFactor);this.g=Math.pow(color.g,gammaFactor);this.b=Math.pow(color.b,gammaFactor);return this}copyLinearToGamma(color,gammaFactor=2){const safeInverse=gammaFactor>0?1/gammaFactor:1;this.r=Math.pow(color.r,safeInverse);this.g=Math.pow(color.g,safeInverse);this.b=Math.pow(color.b,safeInverse);return this}convertGammaToLinear(gammaFactor){this.copyGammaToLinear(this,gammaFactor);return this}convertLinearToGamma(gammaFactor){this.copyLinearToGamma(this,gammaFactor);return this}copySRGBToLinear(color){this.r=SRGBToLinear(color.r);this.g=SRGBToLinear(color.g);this.b=SRGBToLinear(color.b);return this}copyLinearToSRGB(color){this.r=LinearToSRGB(color.r);this.g=LinearToSRGB(color.g);this.b=LinearToSRGB(color.b);return this}convertSRGBToLinear(){this.copySRGBToLinear(this);return this}convertLinearToSRGB(){this.copyLinearToSRGB(this);return this}getHex(){return this.r*255<<16^this.g*255<<8^this.b*255<<0}getHexString(){return("000000"+this.getHex().toString(16)).slice(-6)}getHSL(target){if(target===undefined){console.warn("THREE.Color: .getHSL() target is now required");target={h:0,s:0,l:0}}const r=this.r,g=this.g,b=this.b;const max=Math.max(r,g,b);const min=Math.min(r,g,b);let hue,saturation;const lightness=(min+max)/2;if(min===max){hue=0;saturation=0}else{const delta=max-min;saturation=lightness<=.5?delta/(max+min):delta/(2-max-min);switch(max){case r:hue=(g-b)/delta+(gmax)max=array[i]}return max}let _id=0;const _m1=new Matrix4;const _obj=new Object3D;const _offset=new Vector3;const _box$1=new Box3;const _boxMorphTargets=new Box3;const _vector$8=new Vector3;class BufferGeometry extends EventDispatcher$1{constructor(){super();Object.defineProperty(this,"id",{value:_id++});this.uuid=generateUUID();this.name="";this.type="BufferGeometry";this.index=null;this.attributes={};this.morphAttributes={};this.morphTargetsRelative=false;this.groups=[];this.boundingBox=null;this.boundingSphere=null;this.drawRange={start:0,count:Infinity};this.userData={}}getIndex(){return this.index}setIndex(index){if(Array.isArray(index)){this.index=new(arrayMax(index)>65535?Uint32BufferAttribute:Uint16BufferAttribute)(index,1)}else{this.index=index}return this}getAttribute(name){return this.attributes[name]}setAttribute(name,attribute){this.attributes[name]=attribute;return this}deleteAttribute(name){delete this.attributes[name];return this}hasAttribute(name){return this.attributes[name]!==undefined}addGroup(start,count,materialIndex=0){this.groups.push({start:start,count:count,materialIndex:materialIndex})}clearGroups(){this.groups=[]}setDrawRange(start,count){this.drawRange.start=start;this.drawRange.count=count}applyMatrix4(matrix){const position=this.attributes.position;if(position!==undefined){position.applyMatrix4(matrix);position.needsUpdate=true}const normal=this.attributes.normal;if(normal!==undefined){const normalMatrix=(new Matrix3).getNormalMatrix(matrix);normal.applyNormalMatrix(normalMatrix);normal.needsUpdate=true}const tangent=this.attributes.tangent;if(tangent!==undefined){tangent.transformDirection(matrix);tangent.needsUpdate=true}if(this.boundingBox!==null){this.computeBoundingBox()}if(this.boundingSphere!==null){this.computeBoundingSphere()}return this}rotateX(angle){_m1.makeRotationX(angle);this.applyMatrix4(_m1);return this}rotateY(angle){_m1.makeRotationY(angle);this.applyMatrix4(_m1);return this}rotateZ(angle){_m1.makeRotationZ(angle);this.applyMatrix4(_m1);return this}translate(x,y,z){_m1.makeTranslation(x,y,z);this.applyMatrix4(_m1);return this}scale(x,y,z){_m1.makeScale(x,y,z);this.applyMatrix4(_m1);return this}lookAt(vector){_obj.lookAt(vector);_obj.updateMatrix();this.applyMatrix4(_obj.matrix);return this}center(){this.computeBoundingBox();this.boundingBox.getCenter(_offset).negate();this.translate(_offset.x,_offset.y,_offset.z);return this}setFromPoints(points){const position=[];for(let i=0,l=points.length;i0)data.userData=this.userData;if(this.parameters!==undefined){const parameters=this.parameters;for(const key in parameters){if(parameters[key]!==undefined)data[key]=parameters[key]}return data}data.data={attributes:{}};const index=this.index;if(index!==null){data.data.index={type:index.array.constructor.name,array:Array.prototype.slice.call(index.array)}}const attributes=this.attributes;for(const key in attributes){const attribute=attributes[key];data.data.attributes[key]=attribute.toJSON(data.data)}const morphAttributes={};let hasMorphAttributes=false;for(const key in this.morphAttributes){const attributeArray=this.morphAttributes[key];const array=[];for(let i=0,il=attributeArray.length;i0){morphAttributes[key]=array;hasMorphAttributes=true}}if(hasMorphAttributes){data.data.morphAttributes=morphAttributes;data.data.morphTargetsRelative=this.morphTargetsRelative}const groups=this.groups;if(groups.length>0){data.data.groups=JSON.parse(JSON.stringify(groups))}const boundingSphere=this.boundingSphere;if(boundingSphere!==null){data.data.boundingSphere={center:boundingSphere.center.toArray(),radius:boundingSphere.radius}}return data}clone(){return(new BufferGeometry).copy(this)}copy(source){this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingBox=null;this.boundingSphere=null;const data={};this.name=source.name;const index=source.index;if(index!==null){this.setIndex(index.clone(data))}const attributes=source.attributes;for(const name in attributes){const attribute=attributes[name];this.setAttribute(name,attribute.clone(data))}const morphAttributes=source.morphAttributes;for(const name in morphAttributes){const array=[];const morphAttribute=morphAttributes[name];for(let i=0,l=morphAttribute.length;i0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}}}raycast(raycaster,intersects){const geometry=this.geometry;const material=this.material;const matrixWorld=this.matrixWorld;if(material===undefined)return;if(geometry.boundingSphere===null)geometry.computeBoundingSphere();_sphere$3.copy(geometry.boundingSphere);_sphere$3.applyMatrix4(matrixWorld);if(raycaster.ray.intersectsSphere(_sphere$3)===false)return;_inverseMatrix$2.copy(matrixWorld).invert();_ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2);if(geometry.boundingBox!==null){if(_ray$2.intersectsBox(geometry.boundingBox)===false)return}let intersection;if(geometry.isBufferGeometry){const index=geometry.index;const position=geometry.attributes.position;const morphPosition=geometry.morphAttributes.position;const morphTargetsRelative=geometry.morphTargetsRelative;const uv=geometry.attributes.uv;const uv2=geometry.attributes.uv2;const groups=geometry.groups;const drawRange=geometry.drawRange;if(index!==null){if(Array.isArray(material)){for(let i=0,il=groups.length;iraycaster.far)return null;return{distance:distance,point:_intersectionPointWorld.clone(),object:object}}function checkBufferGeometryIntersection(object,material,raycaster,ray,position,morphPosition,morphTargetsRelative,uv,uv2,a,b,c){_vA$1.fromBufferAttribute(position,a);_vB$1.fromBufferAttribute(position,b);_vC$1.fromBufferAttribute(position,c);const morphInfluences=object.morphTargetInfluences;if(material.morphTargets&&morphPosition&&morphInfluences){_morphA.set(0,0,0);_morphB.set(0,0,0);_morphC.set(0,0,0);for(let i=0,il=morphPosition.length;i0?1:-1;normals.push(vector.x,vector.y,vector.z);uvs.push(ix/gridX);uvs.push(1-iy/gridY);vertexCounter+=1}}for(let iy=0;iy0)data.defines=this.defines;data.vertexShader=this.vertexShader;data.fragmentShader=this.fragmentShader;const extensions={};for(const key in this.extensions){if(this.extensions[key]===true)extensions[key]=true}if(Object.keys(extensions).length>0)data.extensions=extensions;return data}}ShaderMaterial.prototype.isShaderMaterial=true;class Camera extends Object3D{constructor(){super();this.type="Camera";this.matrixWorldInverse=new Matrix4;this.projectionMatrix=new Matrix4;this.projectionMatrixInverse=new Matrix4}copy(source,recursive){super.copy(source,recursive);this.matrixWorldInverse.copy(source.matrixWorldInverse);this.projectionMatrix.copy(source.projectionMatrix);this.projectionMatrixInverse.copy(source.projectionMatrixInverse);return this}getWorldDirection(target){if(target===undefined){console.warn("THREE.Camera: .getWorldDirection() target is now required");target=new Vector3}this.updateWorldMatrix(true,false);const e=this.matrixWorld.elements;return target.set(-e[8],-e[9],-e[10]).normalize()}updateMatrixWorld(force){super.updateMatrixWorld(force);this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(updateParents,updateChildren){super.updateWorldMatrix(updateParents,updateChildren);this.matrixWorldInverse.copy(this.matrixWorld).invert()}clone(){return(new this.constructor).copy(this)}}Camera.prototype.isCamera=true;class PerspectiveCamera extends Camera{constructor(fov=50,aspect=1,near=.1,far=2e3){super();this.type="PerspectiveCamera";this.fov=fov;this.zoom=1;this.near=near;this.far=far;this.focus=10;this.aspect=aspect;this.view=null;this.filmGauge=35;this.filmOffset=0;this.updateProjectionMatrix()}copy(source,recursive){super.copy(source,recursive);this.fov=source.fov;this.zoom=source.zoom;this.near=source.near;this.far=source.far;this.focus=source.focus;this.aspect=source.aspect;this.view=source.view===null?null:Object.assign({},source.view);this.filmGauge=source.filmGauge;this.filmOffset=source.filmOffset;return this}setFocalLength(focalLength){const vExtentSlope=.5*this.getFilmHeight()/focalLength;this.fov=RAD2DEG*2*Math.atan(vExtentSlope);this.updateProjectionMatrix()}getFocalLength(){const vExtentSlope=Math.tan(DEG2RAD*.5*this.fov);return.5*this.getFilmHeight()/vExtentSlope}getEffectiveFOV(){return RAD2DEG*2*Math.atan(Math.tan(DEG2RAD*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}setViewOffset(fullWidth,fullHeight,x,y,width,height){this.aspect=fullWidth/fullHeight;if(this.view===null){this.view={enabled:true,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}}this.view.enabled=true;this.view.fullWidth=fullWidth;this.view.fullHeight=fullHeight;this.view.offsetX=x;this.view.offsetY=y;this.view.width=width;this.view.height=height;this.updateProjectionMatrix()}clearViewOffset(){if(this.view!==null){this.view.enabled=false}this.updateProjectionMatrix()}updateProjectionMatrix(){const near=this.near;let top=near*Math.tan(DEG2RAD*.5*this.fov)/this.zoom;let height=2*top;let width=this.aspect*height;let left=-.5*width;const view=this.view;if(this.view!==null&&this.view.enabled){const fullWidth=view.fullWidth,fullHeight=view.fullHeight;left+=view.offsetX*width/fullWidth;top-=view.offsetY*height/fullHeight;width*=view.width/fullWidth;height*=view.height/fullHeight}const skew=this.filmOffset;if(skew!==0)left+=near*skew/this.getFilmWidth();this.projectionMatrix.makePerspective(left,left+width,top,top-height,near,this.far);this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(meta){const data=super.toJSON(meta);data.object.fov=this.fov;data.object.zoom=this.zoom;data.object.near=this.near;data.object.far=this.far;data.object.focus=this.focus;data.object.aspect=this.aspect;if(this.view!==null)data.object.view=Object.assign({},this.view);data.object.filmGauge=this.filmGauge;data.object.filmOffset=this.filmOffset;return data}}PerspectiveCamera.prototype.isPerspectiveCamera=true;const fov=90,aspect=1;class CubeCamera extends Object3D{constructor(near,far,renderTarget){super();this.type="CubeCamera";if(renderTarget.isWebGLCubeRenderTarget!==true){console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.");return}this.renderTarget=renderTarget;const cameraPX=new PerspectiveCamera(fov,aspect,near,far);cameraPX.layers=this.layers;cameraPX.up.set(0,-1,0);cameraPX.lookAt(new Vector3(1,0,0));this.add(cameraPX);const cameraNX=new PerspectiveCamera(fov,aspect,near,far);cameraNX.layers=this.layers;cameraNX.up.set(0,-1,0);cameraNX.lookAt(new Vector3(-1,0,0));this.add(cameraNX);const cameraPY=new PerspectiveCamera(fov,aspect,near,far);cameraPY.layers=this.layers;cameraPY.up.set(0,0,1);cameraPY.lookAt(new Vector3(0,1,0));this.add(cameraPY);const cameraNY=new PerspectiveCamera(fov,aspect,near,far);cameraNY.layers=this.layers;cameraNY.up.set(0,0,-1);cameraNY.lookAt(new Vector3(0,-1,0));this.add(cameraNY);const cameraPZ=new PerspectiveCamera(fov,aspect,near,far);cameraPZ.layers=this.layers;cameraPZ.up.set(0,-1,0);cameraPZ.lookAt(new Vector3(0,0,1));this.add(cameraPZ);const cameraNZ=new PerspectiveCamera(fov,aspect,near,far);cameraNZ.layers=this.layers;cameraNZ.up.set(0,-1,0);cameraNZ.lookAt(new Vector3(0,0,-1));this.add(cameraNZ)}update(renderer,scene){if(this.parent===null)this.updateMatrixWorld();const renderTarget=this.renderTarget;const[cameraPX,cameraNX,cameraPY,cameraNY,cameraPZ,cameraNZ]=this.children;const currentXrEnabled=renderer.xr.enabled;const currentRenderTarget=renderer.getRenderTarget();renderer.xr.enabled=false;const generateMipmaps=renderTarget.texture.generateMipmaps;renderTarget.texture.generateMipmaps=false;renderer.setRenderTarget(renderTarget,0);renderer.render(scene,cameraPX);renderer.setRenderTarget(renderTarget,1);renderer.render(scene,cameraNX);renderer.setRenderTarget(renderTarget,2);renderer.render(scene,cameraPY);renderer.setRenderTarget(renderTarget,3);renderer.render(scene,cameraNY);renderer.setRenderTarget(renderTarget,4);renderer.render(scene,cameraPZ);renderTarget.texture.generateMipmaps=generateMipmaps;renderer.setRenderTarget(renderTarget,5);renderer.render(scene,cameraNZ);renderer.setRenderTarget(currentRenderTarget);renderer.xr.enabled=currentXrEnabled}}class CubeTexture extends Texture{constructor(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding){images=images!==undefined?images:[];mapping=mapping!==undefined?mapping:CubeReflectionMapping;format=format!==undefined?format:RGBFormat;super(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this._needsFlipEnvMap=true;this.flipY=false}get images(){return this.image}set images(value){this.image=value}}CubeTexture.prototype.isCubeTexture=true;class WebGLCubeRenderTarget extends WebGLRenderTarget{constructor(size,options,dummy){if(Number.isInteger(options)){console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )");options=dummy}super(size,size,options);options=options||{};this.texture=new CubeTexture(undefined,options.mapping,options.wrapS,options.wrapT,options.magFilter,options.minFilter,options.format,options.type,options.anisotropy,options.encoding);this.texture.generateMipmaps=options.generateMipmaps!==undefined?options.generateMipmaps:false;this.texture.minFilter=options.minFilter!==undefined?options.minFilter:LinearFilter;this.texture._needsFlipEnvMap=false}fromEquirectangularTexture(renderer,texture){this.texture.type=texture.type;this.texture.format=RGBAFormat;this.texture.encoding=texture.encoding;this.texture.generateMipmaps=texture.generateMipmaps;this.texture.minFilter=texture.minFilter;this.texture.magFilter=texture.magFilter;const shader={uniforms:{tEquirect:{value:null}},vertexShader:`\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t`,fragmentShader:`\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t`};const geometry=new BoxGeometry(5,5,5);const material=new ShaderMaterial({name:"CubemapFromEquirect",uniforms:cloneUniforms(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader,side:BackSide,blending:NoBlending});material.uniforms.tEquirect.value=texture;const mesh=new Mesh(geometry,material);const currentMinFilter=texture.minFilter;if(texture.minFilter===LinearMipmapLinearFilter)texture.minFilter=LinearFilter;const camera=new CubeCamera(1,10,this);camera.update(renderer,mesh);texture.minFilter=currentMinFilter;mesh.geometry.dispose();mesh.material.dispose();return this}clear(renderer,color,depth,stencil){const currentRenderTarget=renderer.getRenderTarget();for(let i=0;i<6;i++){renderer.setRenderTarget(this,i);renderer.clear(color,depth,stencil)}renderer.setRenderTarget(currentRenderTarget)}}WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget=true;class DataTexture extends Texture{constructor(data,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy,encoding){super(null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this.image={data:data||null,width:width||1,height:height||1};this.magFilter=magFilter!==undefined?magFilter:NearestFilter;this.minFilter=minFilter!==undefined?minFilter:NearestFilter;this.generateMipmaps=false;this.flipY=false;this.unpackAlignment=1;this.needsUpdate=true}}DataTexture.prototype.isDataTexture=true;const _sphere$2=new Sphere;const _vector$7=new Vector3;class Frustum{constructor(p0=new Plane,p1=new Plane,p2=new Plane,p3=new Plane,p4=new Plane,p5=new Plane){this.planes=[p0,p1,p2,p3,p4,p5]}set(p0,p1,p2,p3,p4,p5){const planes=this.planes;planes[0].copy(p0);planes[1].copy(p1);planes[2].copy(p2);planes[3].copy(p3);planes[4].copy(p4);planes[5].copy(p5);return this}copy(frustum){const planes=this.planes;for(let i=0;i<6;i++){planes[i].copy(frustum.planes[i])}return this}setFromProjectionMatrix(m){const planes=this.planes;const me=m.elements;const me0=me[0],me1=me[1],me2=me[2],me3=me[3];const me4=me[4],me5=me[5],me6=me[6],me7=me[7];const me8=me[8],me9=me[9],me10=me[10],me11=me[11];const me12=me[12],me13=me[13],me14=me[14],me15=me[15];planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize();planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize();planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize();planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize();planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize();planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize();return this}intersectsObject(object){const geometry=object.geometry;if(geometry.boundingSphere===null)geometry.computeBoundingSphere();_sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);return this.intersectsSphere(_sphere$2)}intersectsSprite(sprite){_sphere$2.center.set(0,0,0);_sphere$2.radius=.7071067811865476;_sphere$2.applyMatrix4(sprite.matrixWorld);return this.intersectsSphere(_sphere$2)}intersectsSphere(sphere){const planes=this.planes;const center=sphere.center;const negRadius=-sphere.radius;for(let i=0;i<6;i++){const distance=planes[i].distanceToPoint(center);if(distance0?box.max.x:box.min.x;_vector$7.y=plane.normal.y>0?box.max.y:box.min.y;_vector$7.z=plane.normal.z>0?box.max.z:box.min.z;if(plane.distanceToPoint(_vector$7)<0){return false}}return true}containsPoint(point){const planes=this.planes;for(let i=0;i<6;i++){if(planes[i].distanceToPoint(point)<0){return false}}return true}clone(){return(new this.constructor).copy(this)}}function WebGLAnimation(){let context=null;let isAnimating=false;let animationLoop=null;let requestId=null;function onAnimationFrame(time,frame){animationLoop(time,frame);requestId=context.requestAnimationFrame(onAnimationFrame)}return{start:function(){if(isAnimating===true)return;if(animationLoop===null)return;requestId=context.requestAnimationFrame(onAnimationFrame);isAnimating=true},stop:function(){context.cancelAnimationFrame(requestId);isAnimating=false},setAnimationLoop:function(callback){animationLoop=callback},setContext:function(value){context=value}}}function WebGLAttributes(gl,capabilities){const isWebGL2=capabilities.isWebGL2;const buffers=new WeakMap;function createBuffer(attribute,bufferType){const array=attribute.array;const usage=attribute.usage;const buffer=gl.createBuffer();gl.bindBuffer(bufferType,buffer);gl.bufferData(bufferType,array,usage);attribute.onUploadCallback();let type=5126;if(array instanceof Float32Array){type=5126}else if(array instanceof Float64Array){console.warn("THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.")}else if(array instanceof Uint16Array){if(attribute.isFloat16BufferAttribute){if(isWebGL2){type=5131}else{console.warn("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.")}}else{type=5123}}else if(array instanceof Int16Array){type=5122}else if(array instanceof Uint32Array){type=5125}else if(array instanceof Int32Array){type=5124}else if(array instanceof Int8Array){type=5120}else if(array instanceof Uint8Array){type=5121}return{buffer:buffer,type:type,bytesPerElement:array.BYTES_PER_ELEMENT,version:attribute.version}}function updateBuffer(buffer,attribute,bufferType){const array=attribute.array;const updateRange=attribute.updateRange;gl.bindBuffer(bufferType,buffer);if(updateRange.count===-1){gl.bufferSubData(bufferType,0,array)}else{if(isWebGL2){gl.bufferSubData(bufferType,updateRange.offset*array.BYTES_PER_ELEMENT,array,updateRange.offset,updateRange.count)}else{gl.bufferSubData(bufferType,updateRange.offset*array.BYTES_PER_ELEMENT,array.subarray(updateRange.offset,updateRange.offset+updateRange.count))}updateRange.count=-1}}function get(attribute){if(attribute.isInterleavedBufferAttribute)attribute=attribute.data;return buffers.get(attribute)}function remove(attribute){if(attribute.isInterleavedBufferAttribute)attribute=attribute.data;const data=buffers.get(attribute);if(data){gl.deleteBuffer(data.buffer);buffers.delete(attribute)}}function update(attribute,bufferType){if(attribute.isGLBufferAttribute){const cached=buffers.get(attribute);if(!cached||cached.version=0){const geometryAttribute=geometryAttributes[name];if(geometryAttribute!==undefined){const normalized=geometryAttribute.normalized;const size=geometryAttribute.itemSize;const attribute=attributes.get(geometryAttribute);if(attribute===undefined)continue;const buffer=attribute.buffer;const type=attribute.type;const bytesPerElement=attribute.bytesPerElement;if(geometryAttribute.isInterleavedBufferAttribute){const data=geometryAttribute.data;const stride=data.stride;const offset=geometryAttribute.offset;if(data&&data.isInstancedInterleavedBuffer){enableAttributeAndDivisor(programAttribute,data.meshPerAttribute);if(geometry._maxInstanceCount===undefined){geometry._maxInstanceCount=data.meshPerAttribute*data.count}}else{enableAttribute(programAttribute)}gl.bindBuffer(34962,buffer);vertexAttribPointer(programAttribute,size,type,normalized,stride*bytesPerElement,offset*bytesPerElement)}else{if(geometryAttribute.isInstancedBufferAttribute){enableAttributeAndDivisor(programAttribute,geometryAttribute.meshPerAttribute);if(geometry._maxInstanceCount===undefined){geometry._maxInstanceCount=geometryAttribute.meshPerAttribute*geometryAttribute.count}}else{enableAttribute(programAttribute)}gl.bindBuffer(34962,buffer);vertexAttribPointer(programAttribute,size,type,normalized,0,0)}}else if(name==="instanceMatrix"){const attribute=attributes.get(object.instanceMatrix);if(attribute===undefined)continue;const buffer=attribute.buffer;const type=attribute.type;enableAttributeAndDivisor(programAttribute+0,1);enableAttributeAndDivisor(programAttribute+1,1);enableAttributeAndDivisor(programAttribute+2,1);enableAttributeAndDivisor(programAttribute+3,1);gl.bindBuffer(34962,buffer);gl.vertexAttribPointer(programAttribute+0,4,type,false,64,0);gl.vertexAttribPointer(programAttribute+1,4,type,false,64,16);gl.vertexAttribPointer(programAttribute+2,4,type,false,64,32);gl.vertexAttribPointer(programAttribute+3,4,type,false,64,48)}else if(name==="instanceColor"){const attribute=attributes.get(object.instanceColor);if(attribute===undefined)continue;const buffer=attribute.buffer;const type=attribute.type;enableAttributeAndDivisor(programAttribute,1);gl.bindBuffer(34962,buffer);gl.vertexAttribPointer(programAttribute,3,type,false,12,0)}else if(materialDefaultAttributeValues!==undefined){const value=materialDefaultAttributeValues[name];if(value!==undefined){switch(value.length){case 2:gl.vertexAttrib2fv(programAttribute,value);break;case 3:gl.vertexAttrib3fv(programAttribute,value);break;case 4:gl.vertexAttrib4fv(programAttribute,value);break;default:gl.vertexAttrib1fv(programAttribute,value)}}}}}disableUnusedAttributes()}function dispose(){reset();for(const geometryId in bindingStates){const programMap=bindingStates[geometryId];for(const programId in programMap){const stateMap=programMap[programId];for(const wireframe in stateMap){deleteVertexArrayObject(stateMap[wireframe].object);delete stateMap[wireframe]}delete programMap[programId]}delete bindingStates[geometryId]}}function releaseStatesOfGeometry(geometry){if(bindingStates[geometry.id]===undefined)return;const programMap=bindingStates[geometry.id];for(const programId in programMap){const stateMap=programMap[programId];for(const wireframe in stateMap){deleteVertexArrayObject(stateMap[wireframe].object);delete stateMap[wireframe]}delete programMap[programId]}delete bindingStates[geometry.id]}function releaseStatesOfProgram(program){for(const geometryId in bindingStates){const programMap=bindingStates[geometryId];if(programMap[program.id]===undefined)continue;const stateMap=programMap[program.id];for(const wireframe in stateMap){deleteVertexArrayObject(stateMap[wireframe].object);delete stateMap[wireframe]}delete programMap[program.id]}}function reset(){resetDefaultState();if(currentState===defaultState)return;currentState=defaultState;bindVertexArrayObject(currentState.object)}function resetDefaultState(){defaultState.geometry=null;defaultState.program=null;defaultState.wireframe=false}return{setup:setup,reset:reset,resetDefaultState:resetDefaultState,dispose:dispose,releaseStatesOfGeometry:releaseStatesOfGeometry,releaseStatesOfProgram:releaseStatesOfProgram,initAttributes:initAttributes,enableAttribute:enableAttribute,disableUnusedAttributes:disableUnusedAttributes}}function WebGLBufferRenderer(gl,extensions,info,capabilities){const isWebGL2=capabilities.isWebGL2;let mode;function setMode(value){mode=value}function render(start,count){gl.drawArrays(mode,start,count);info.update(count,mode,1)}function renderInstances(start,count,primcount){if(primcount===0)return;let extension,methodName;if(isWebGL2){extension=gl;methodName="drawArraysInstanced"}else{extension=extensions.get("ANGLE_instanced_arrays");methodName="drawArraysInstancedANGLE";if(extension===null){console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");return}}extension[methodName](mode,start,count,primcount);info.update(count,mode,primcount)}this.setMode=setMode;this.render=render;this.renderInstances=renderInstances}function WebGLCapabilities(gl,extensions,parameters){let maxAnisotropy;function getMaxAnisotropy(){if(maxAnisotropy!==undefined)return maxAnisotropy;if(extensions.has("EXT_texture_filter_anisotropic")===true){const extension=extensions.get("EXT_texture_filter_anisotropic");maxAnisotropy=gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else{maxAnisotropy=0}return maxAnisotropy}function getMaxPrecision(precision){if(precision==="highp"){if(gl.getShaderPrecisionFormat(35633,36338).precision>0&&gl.getShaderPrecisionFormat(35632,36338).precision>0){return"highp"}precision="mediump"}if(precision==="mediump"){if(gl.getShaderPrecisionFormat(35633,36337).precision>0&&gl.getShaderPrecisionFormat(35632,36337).precision>0){return"mediump"}}return"lowp"}const isWebGL2=typeof WebGL2RenderingContext!=="undefined"&&gl instanceof WebGL2RenderingContext||typeof WebGL2ComputeRenderingContext!=="undefined"&&gl instanceof WebGL2ComputeRenderingContext;let precision=parameters.precision!==undefined?parameters.precision:"highp";const maxPrecision=getMaxPrecision(precision);if(maxPrecision!==precision){console.warn("THREE.WebGLRenderer:",precision,"not supported, using",maxPrecision,"instead.");precision=maxPrecision}const logarithmicDepthBuffer=parameters.logarithmicDepthBuffer===true;const maxTextures=gl.getParameter(34930);const maxVertexTextures=gl.getParameter(35660);const maxTextureSize=gl.getParameter(3379);const maxCubemapSize=gl.getParameter(34076);const maxAttributes=gl.getParameter(34921);const maxVertexUniforms=gl.getParameter(36347);const maxVaryings=gl.getParameter(36348);const maxFragmentUniforms=gl.getParameter(36349);const vertexTextures=maxVertexTextures>0;const floatFragmentTextures=isWebGL2||extensions.has("OES_texture_float");const floatVertexTextures=vertexTextures&&floatFragmentTextures;const maxSamples=isWebGL2?gl.getParameter(36183):0;return{isWebGL2:isWebGL2,getMaxAnisotropy:getMaxAnisotropy,getMaxPrecision:getMaxPrecision,precision:precision,logarithmicDepthBuffer:logarithmicDepthBuffer,maxTextures:maxTextures,maxVertexTextures:maxVertexTextures,maxTextureSize:maxTextureSize,maxCubemapSize:maxCubemapSize,maxAttributes:maxAttributes,maxVertexUniforms:maxVertexUniforms,maxVaryings:maxVaryings,maxFragmentUniforms:maxFragmentUniforms,vertexTextures:vertexTextures,floatFragmentTextures:floatFragmentTextures,floatVertexTextures:floatVertexTextures,maxSamples:maxSamples}}function WebGLClipping(properties){const scope=this;let globalState=null,numGlobalPlanes=0,localClippingEnabled=false,renderingShadows=false;const plane=new Plane,viewNormalMatrix=new Matrix3,uniform={value:null,needsUpdate:false};this.uniform=uniform;this.numPlanes=0;this.numIntersection=0;this.init=function(planes,enableLocalClipping,camera){const enabled=planes.length!==0||enableLocalClipping||numGlobalPlanes!==0||localClippingEnabled;localClippingEnabled=enableLocalClipping;globalState=projectPlanes(planes,camera,0);numGlobalPlanes=planes.length;return enabled};this.beginShadows=function(){renderingShadows=true;projectPlanes(null)};this.endShadows=function(){renderingShadows=false;resetGlobalState()};this.setState=function(material,camera,useCache){const planes=material.clippingPlanes,clipIntersection=material.clipIntersection,clipShadows=material.clipShadows;const materialProperties=properties.get(material);if(!localClippingEnabled||planes===null||planes.length===0||renderingShadows&&!clipShadows){if(renderingShadows){projectPlanes(null)}else{resetGlobalState()}}else{const nGlobal=renderingShadows?0:numGlobalPlanes,lGlobal=nGlobal*4;let dstArray=materialProperties.clippingState||null;uniform.value=dstArray;dstArray=projectPlanes(planes,camera,lGlobal,useCache);for(let i=0;i!==lGlobal;++i){dstArray[i]=globalState[i]}materialProperties.clippingState=dstArray;this.numIntersection=clipIntersection?this.numPlanes:0;this.numPlanes+=nGlobal}};function resetGlobalState(){if(uniform.value!==globalState){uniform.value=globalState;uniform.needsUpdate=numGlobalPlanes>0}scope.numPlanes=numGlobalPlanes;scope.numIntersection=0}function projectPlanes(planes,camera,dstOffset,skipTransform){const nPlanes=planes!==null?planes.length:0;let dstArray=null;if(nPlanes!==0){dstArray=uniform.value;if(skipTransform!==true||dstArray===null){const flatSize=dstOffset+nPlanes*4,viewMatrix=camera.matrixWorldInverse;viewNormalMatrix.getNormalMatrix(viewMatrix);if(dstArray===null||dstArray.length0){const currentRenderTarget=renderer.getRenderTarget();const renderTarget=new WebGLCubeRenderTarget(image.height/2);renderTarget.fromEquirectangularTexture(renderer,texture);cubemaps.set(texture,renderTarget);renderer.setRenderTarget(currentRenderTarget);texture.addEventListener("dispose",onTextureDispose);return mapTextureMapping(renderTarget.texture,texture.mapping)}else{return null}}}}return texture}function onTextureDispose(event){const texture=event.target;texture.removeEventListener("dispose",onTextureDispose);const cubemap=cubemaps.get(texture);if(cubemap!==undefined){cubemaps.delete(texture);cubemap.dispose()}}function dispose(){cubemaps=new WeakMap}return{get:get,dispose:dispose}}function WebGLExtensions(gl){const extensions={};function getExtension(name){if(extensions[name]!==undefined){return extensions[name]}let extension;switch(name){case"WEBGL_depth_texture":extension=gl.getExtension("WEBGL_depth_texture")||gl.getExtension("MOZ_WEBGL_depth_texture")||gl.getExtension("WEBKIT_WEBGL_depth_texture");break;case"EXT_texture_filter_anisotropic":extension=gl.getExtension("EXT_texture_filter_anisotropic")||gl.getExtension("MOZ_EXT_texture_filter_anisotropic")||gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":extension=gl.getExtension("WEBGL_compressed_texture_s3tc")||gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":extension=gl.getExtension("WEBGL_compressed_texture_pvrtc")||gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:extension=gl.getExtension(name)}extensions[name]=extension;return extension}return{has:function(name){return getExtension(name)!==null},init:function(capabilities){if(capabilities.isWebGL2){getExtension("EXT_color_buffer_float")}else{getExtension("WEBGL_depth_texture");getExtension("OES_texture_float");getExtension("OES_texture_half_float");getExtension("OES_texture_half_float_linear");getExtension("OES_standard_derivatives");getExtension("OES_element_index_uint");getExtension("OES_vertex_array_object");getExtension("ANGLE_instanced_arrays")}getExtension("OES_texture_float_linear");getExtension("EXT_color_buffer_half_float")},get:function(name){const extension=getExtension(name);if(extension===null){console.warn("THREE.WebGLRenderer: "+name+" extension not supported.")}return extension}}}function WebGLGeometries(gl,attributes,info,bindingStates){const geometries={};const wireframeAttributes=new WeakMap;function onGeometryDispose(event){const geometry=event.target;if(geometry.index!==null){attributes.remove(geometry.index)}for(const name in geometry.attributes){attributes.remove(geometry.attributes[name])}geometry.removeEventListener("dispose",onGeometryDispose);delete geometries[geometry.id];const attribute=wireframeAttributes.get(geometry);if(attribute){attributes.remove(attribute);wireframeAttributes.delete(geometry)}bindingStates.releaseStatesOfGeometry(geometry);if(geometry.isInstancedBufferGeometry===true){delete geometry._maxInstanceCount}info.memory.geometries--}function get(object,geometry){if(geometries[geometry.id]===true)return geometry;geometry.addEventListener("dispose",onGeometryDispose);geometries[geometry.id]=true;info.memory.geometries++;return geometry}function update(geometry){const geometryAttributes=geometry.attributes;for(const name in geometryAttributes){attributes.update(geometryAttributes[name],34962)}const morphAttributes=geometry.morphAttributes;for(const name in morphAttributes){const array=morphAttributes[name];for(let i=0,l=array.length;i65535?Uint32BufferAttribute:Uint16BufferAttribute)(indices,1);attribute.version=version;const previousAttribute=wireframeAttributes.get(geometry);if(previousAttribute)attributes.remove(previousAttribute);wireframeAttributes.set(geometry,attribute)}function getWireframeAttribute(geometry){const currentAttribute=wireframeAttributes.get(geometry);if(currentAttribute){const geometryIndex=geometry.index;if(geometryIndex!==null){if(currentAttribute.version0)return array;const n=nBlocks*blockSize;let r=arrayCacheF32[n];if(r===undefined){r=new Float32Array(n);arrayCacheF32[n]=r}if(nBlocks!==0){firstElem.toArray(r,0);for(let i=1,offset=0;i!==nBlocks;++i){offset+=blockSize;array[i].toArray(r,offset)}}return r}function arraysEqual(a,b){if(a.length!==b.length)return false;for(let i=0,l=a.length;i/gm;function resolveIncludes(string){return string.replace(includePattern,includeReplacer)}function includeReplacer(match,include){const string=ShaderChunk[include];if(string===undefined){throw new Error("Can not resolve #include <"+include+">")}return resolveIncludes(string)}const deprecatedUnrollLoopPattern=/#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;const unrollLoopPattern=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function unrollLoops(string){return string.replace(unrollLoopPattern,loopReplacer).replace(deprecatedUnrollLoopPattern,deprecatedLoopReplacer)}function deprecatedLoopReplacer(match,start,end,snippet){console.warn("WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.");return loopReplacer(match,start,end,snippet)}function loopReplacer(match,start,end,snippet){let string="";for(let i=parseInt(start);i0?renderer.gammaFactor:1;const customExtensions=parameters.isWebGL2?"":generateExtensions(parameters);const customDefines=generateDefines(defines);const program=gl.createProgram();let prefixVertex,prefixFragment;let versionString=parameters.glslVersion?"#version "+parameters.glslVersion+"\n":"";if(parameters.isRawShaderMaterial){prefixVertex=[customDefines].filter(filterEmptyLine).join("\n");if(prefixVertex.length>0){prefixVertex+="\n"}prefixFragment=[customExtensions,customDefines].filter(filterEmptyLine).join("\n");if(prefixFragment.length>0){prefixFragment+="\n"}}else{prefixVertex=[generatePrecision(parameters),"#define SHADER_NAME "+parameters.shaderName,customDefines,parameters.instancing?"#define USE_INSTANCING":"",parameters.instancingColor?"#define USE_INSTANCING_COLOR":"",parameters.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+gammaFactorDefine,"#define MAX_BONES "+parameters.maxBones,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp2?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.normalMap&¶meters.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",parameters.normalMap&¶meters.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",parameters.clearcoatMap?"#define USE_CLEARCOATMAP":"",parameters.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",parameters.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",parameters.displacementMap&¶meters.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.roughnessMap?"#define USE_ROUGHNESSMAP":"",parameters.metalnessMap?"#define USE_METALNESSMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.transmissionMap?"#define USE_TRANSMISSIONMAP":"",parameters.vertexTangents?"#define USE_TANGENT":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.vertexAlphas?"#define USE_COLOR_ALPHA":"",parameters.vertexUvs?"#define USE_UV":"",parameters.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.skinning?"#define USE_SKINNING":"",parameters.useVertexTexture?"#define BONE_TEXTURE":"",parameters.morphTargets?"#define USE_MORPHTARGETS":"",parameters.morphNormals&¶meters.flatShading===false?"#define USE_MORPHNORMALS":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.sizeAttenuation?"#define USE_SIZEATTENUATION":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&¶meters.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING","\tattribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR","\tattribute vec3 instanceColor;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT","\tattribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )","\tattribute vec4 color;","#elif defined( USE_COLOR )","\tattribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS","\tattribute vec3 morphTarget0;","\tattribute vec3 morphTarget1;","\tattribute vec3 morphTarget2;","\tattribute vec3 morphTarget3;","\t#ifdef USE_MORPHNORMALS","\t\tattribute vec3 morphNormal0;","\t\tattribute vec3 morphNormal1;","\t\tattribute vec3 morphNormal2;","\t\tattribute vec3 morphNormal3;","\t#else","\t\tattribute vec3 morphTarget4;","\t\tattribute vec3 morphTarget5;","\t\tattribute vec3 morphTarget6;","\t\tattribute vec3 morphTarget7;","\t#endif","#endif","#ifdef USE_SKINNING","\tattribute vec4 skinIndex;","\tattribute vec4 skinWeight;","#endif","\n"].filter(filterEmptyLine).join("\n");prefixFragment=[customExtensions,generatePrecision(parameters),"#define SHADER_NAME "+parameters.shaderName,customDefines,parameters.alphaTest?"#define ALPHATEST "+parameters.alphaTest+(parameters.alphaTest%1?"":".0"):"","#define GAMMA_FACTOR "+gammaFactorDefine,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp2?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.matcap?"#define USE_MATCAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapTypeDefine:"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.envMap?"#define "+envMapBlendingDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.normalMap&¶meters.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",parameters.normalMap&¶meters.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",parameters.clearcoatMap?"#define USE_CLEARCOATMAP":"",parameters.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",parameters.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.roughnessMap?"#define USE_ROUGHNESSMAP":"",parameters.metalnessMap?"#define USE_METALNESSMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.sheen?"#define USE_SHEEN":"",parameters.transmissionMap?"#define USE_TRANSMISSIONMAP":"",parameters.vertexTangents?"#define USE_TANGENT":"",parameters.vertexColors||parameters.instancingColor?"#define USE_COLOR":"",parameters.vertexAlphas?"#define USE_COLOR_ALPHA":"",parameters.vertexUvs?"#define USE_UV":"",parameters.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",parameters.gradientMap?"#define USE_GRADIENTMAP":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",parameters.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&¶meters.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"",(parameters.extensionShaderTextureLOD||parameters.envMap)&¶meters.rendererExtensionShaderTextureLod?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",parameters.toneMapping!==NoToneMapping?"#define TONE_MAPPING":"",parameters.toneMapping!==NoToneMapping?ShaderChunk["tonemapping_pars_fragment"]:"",parameters.toneMapping!==NoToneMapping?getToneMappingFunction("toneMapping",parameters.toneMapping):"",parameters.dithering?"#define DITHERING":"",ShaderChunk["encodings_pars_fragment"],parameters.map?getTexelDecodingFunction("mapTexelToLinear",parameters.mapEncoding):"",parameters.matcap?getTexelDecodingFunction("matcapTexelToLinear",parameters.matcapEncoding):"",parameters.envMap?getTexelDecodingFunction("envMapTexelToLinear",parameters.envMapEncoding):"",parameters.emissiveMap?getTexelDecodingFunction("emissiveMapTexelToLinear",parameters.emissiveMapEncoding):"",parameters.lightMap?getTexelDecodingFunction("lightMapTexelToLinear",parameters.lightMapEncoding):"",getTexelEncodingFunction("linearToOutputTexel",parameters.outputEncoding),parameters.depthPacking?"#define DEPTH_PACKING "+parameters.depthPacking:"","\n"].filter(filterEmptyLine).join("\n")}vertexShader=resolveIncludes(vertexShader);vertexShader=replaceLightNums(vertexShader,parameters);vertexShader=replaceClippingPlaneNums(vertexShader,parameters);fragmentShader=resolveIncludes(fragmentShader);fragmentShader=replaceLightNums(fragmentShader,parameters);fragmentShader=replaceClippingPlaneNums(fragmentShader,parameters);vertexShader=unrollLoops(vertexShader);fragmentShader=unrollLoops(fragmentShader);if(parameters.isWebGL2&¶meters.isRawShaderMaterial!==true){versionString="#version 300 es\n";prefixVertex=["#define attribute in","#define varying out","#define texture2D texture"].join("\n")+"\n"+prefixVertex;prefixFragment=["#define varying in",parameters.glslVersion===GLSL3?"":"out highp vec4 pc_fragColor;",parameters.glslVersion===GLSL3?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join("\n")+"\n"+prefixFragment}const vertexGlsl=versionString+prefixVertex+vertexShader;const fragmentGlsl=versionString+prefixFragment+fragmentShader;const glVertexShader=WebGLShader(gl,35633,vertexGlsl);const glFragmentShader=WebGLShader(gl,35632,fragmentGlsl);gl.attachShader(program,glVertexShader);gl.attachShader(program,glFragmentShader);if(parameters.index0AttributeName!==undefined){gl.bindAttribLocation(program,0,parameters.index0AttributeName)}else if(parameters.morphTargets===true){gl.bindAttribLocation(program,0,"position")}gl.linkProgram(program);if(renderer.debug.checkShaderErrors){const programLog=gl.getProgramInfoLog(program).trim();const vertexLog=gl.getShaderInfoLog(glVertexShader).trim();const fragmentLog=gl.getShaderInfoLog(glFragmentShader).trim();let runnable=true;let haveDiagnostics=true;if(gl.getProgramParameter(program,35714)===false){runnable=false;const vertexErrors=getShaderErrors(gl,glVertexShader,"vertex");const fragmentErrors=getShaderErrors(gl,glFragmentShader,"fragment");console.error("THREE.WebGLProgram: shader error: ",gl.getError(),"35715",gl.getProgramParameter(program,35715),"gl.getProgramInfoLog",programLog,vertexErrors,fragmentErrors)}else if(programLog!==""){console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",programLog)}else if(vertexLog===""||fragmentLog===""){haveDiagnostics=false}if(haveDiagnostics){this.diagnostics={runnable:runnable,programLog:programLog,vertexShader:{log:vertexLog,prefix:prefixVertex},fragmentShader:{log:fragmentLog,prefix:prefixFragment}}}}gl.deleteShader(glVertexShader);gl.deleteShader(glFragmentShader);let cachedUniforms;this.getUniforms=function(){if(cachedUniforms===undefined){cachedUniforms=new WebGLUniforms(gl,program)}return cachedUniforms};let cachedAttributes;this.getAttributes=function(){if(cachedAttributes===undefined){cachedAttributes=fetchAttributeLocations(gl,program)}return cachedAttributes};this.destroy=function(){bindingStates.releaseStatesOfProgram(this);gl.deleteProgram(program);this.program=undefined};this.name=parameters.shaderName;this.id=programIdCount++;this.cacheKey=cacheKey;this.usedTimes=1;this.program=program;this.vertexShader=glVertexShader;this.fragmentShader=glFragmentShader;return this}function WebGLPrograms(renderer,cubemaps,extensions,capabilities,bindingStates,clipping){const programs=[];const isWebGL2=capabilities.isWebGL2;const logarithmicDepthBuffer=capabilities.logarithmicDepthBuffer;const floatVertexTextures=capabilities.floatVertexTextures;const maxVertexUniforms=capabilities.maxVertexUniforms;const vertexTextures=capabilities.vertexTextures;let precision=capabilities.precision;const shaderIDs={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};const parameterNames=["precision","isWebGL2","supportsVertexTextures","outputEncoding","instancing","instancingColor","map","mapEncoding","matcap","matcapEncoding","envMap","envMapMode","envMapEncoding","envMapCubeUV","lightMap","lightMapEncoding","aoMap","emissiveMap","emissiveMapEncoding","bumpMap","normalMap","objectSpaceNormalMap","tangentSpaceNormalMap","clearcoatMap","clearcoatRoughnessMap","clearcoatNormalMap","displacementMap","specularMap","roughnessMap","metalnessMap","gradientMap","alphaMap","combine","vertexColors","vertexAlphas","vertexTangents","vertexUvs","uvsVertexOnly","fog","useFog","fogExp2","flatShading","sizeAttenuation","logarithmicDepthBuffer","skinning","maxBones","useVertexTexture","morphTargets","morphNormals","premultipliedAlpha","numDirLights","numPointLights","numSpotLights","numHemiLights","numRectAreaLights","numDirLightShadows","numPointLightShadows","numSpotLightShadows","shadowMapEnabled","shadowMapType","toneMapping","physicallyCorrectLights","alphaTest","doubleSided","flipSided","numClippingPlanes","numClipIntersection","depthPacking","dithering","sheen","transmissionMap"];function getMaxBones(object){const skeleton=object.skeleton;const bones=skeleton.bones;if(floatVertexTextures){return 1024}else{const nVertexUniforms=maxVertexUniforms;const nVertexMatrices=Math.floor((nVertexUniforms-20)/4);const maxBones=Math.min(nVertexMatrices,bones.length);if(maxBones0,maxBones:maxBones,useVertexTexture:floatVertexTextures,morphTargets:material.morphTargets,morphNormals:material.morphNormals,numDirLights:lights.directional.length,numPointLights:lights.point.length,numSpotLights:lights.spot.length,numRectAreaLights:lights.rectArea.length,numHemiLights:lights.hemi.length,numDirLightShadows:lights.directionalShadowMap.length,numPointLightShadows:lights.pointShadowMap.length,numSpotLightShadows:lights.spotShadowMap.length,numClippingPlanes:clipping.numPlanes,numClipIntersection:clipping.numIntersection,dithering:material.dithering,shadowMapEnabled:renderer.shadowMap.enabled&&shadows.length>0,shadowMapType:renderer.shadowMap.type,toneMapping:material.toneMapped?renderer.toneMapping:NoToneMapping,physicallyCorrectLights:renderer.physicallyCorrectLights,premultipliedAlpha:material.premultipliedAlpha,alphaTest:material.alphaTest,doubleSided:material.side===DoubleSide,flipSided:material.side===BackSide,depthPacking:material.depthPacking!==undefined?material.depthPacking:false,index0AttributeName:material.index0AttributeName,extensionDerivatives:material.extensions&&material.extensions.derivatives,extensionFragDepth:material.extensions&&material.extensions.fragDepth,extensionDrawBuffers:material.extensions&&material.extensions.drawBuffers,extensionShaderTextureLOD:material.extensions&&material.extensions.shaderTextureLOD,rendererExtensionFragDepth:isWebGL2||extensions.has("EXT_frag_depth"),rendererExtensionDrawBuffers:isWebGL2||extensions.has("WEBGL_draw_buffers"),rendererExtensionShaderTextureLod:isWebGL2||extensions.has("EXT_shader_texture_lod"),customProgramCacheKey:material.customProgramCacheKey()};return parameters}function getProgramCacheKey(parameters){const array=[];if(parameters.shaderID){array.push(parameters.shaderID)}else{array.push(parameters.fragmentShader);array.push(parameters.vertexShader)}if(parameters.defines!==undefined){for(const name in parameters.defines){array.push(name);array.push(parameters.defines[name])}}if(parameters.isRawShaderMaterial===false){for(let i=0;i1)opaque.sort(customOpaqueSort||painterSortStable);if(transparent.length>1)transparent.sort(customTransparentSort||reversePainterSortStable)}function finish(){for(let i=renderItemsIndex,il=renderItems.length;i=lists.get(scene).length){list=new WebGLRenderList(properties);lists.get(scene).push(list)}else{list=lists.get(scene)[renderCallDepth]}}return list}function dispose(){lists=new WeakMap}return{get:get,dispose:dispose}}function UniformsCache(){const lights={};return{get:function(light){if(lights[light.id]!==undefined){return lights[light.id]}let uniforms;switch(light.type){case"DirectionalLight":uniforms={direction:new Vector3,color:new Color};break;case"SpotLight":uniforms={position:new Vector3,direction:new Vector3,color:new Color,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":uniforms={position:new Vector3,color:new Color,distance:0,decay:0};break;case"HemisphereLight":uniforms={direction:new Vector3,skyColor:new Color,groundColor:new Color};break;case"RectAreaLight":uniforms={color:new Color,position:new Vector3,halfWidth:new Vector3,halfHeight:new Vector3};break}lights[light.id]=uniforms;return uniforms}}}function ShadowUniformsCache(){const lights={};return{get:function(light){if(lights[light.id]!==undefined){return lights[light.id]}let uniforms;switch(light.type){case"DirectionalLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2};break;case"SpotLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2};break;case"PointLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2,shadowCameraNear:1,shadowCameraFar:1e3};break}lights[light.id]=uniforms;return uniforms}}}let nextVersion=0;function shadowCastingLightsFirst(lightA,lightB){return(lightB.castShadow?1:0)-(lightA.castShadow?1:0)}function WebGLLights(extensions,capabilities){const cache=new UniformsCache;const shadowCache=ShadowUniformsCache();const state={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotShadow:[],spotShadowMap:[],spotShadowMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[]};for(let i=0;i<9;i++)state.probe.push(new Vector3);const vector3=new Vector3;const matrix4=new Matrix4;const matrix42=new Matrix4;function setup(lights){let r=0,g=0,b=0;for(let i=0;i<9;i++)state.probe[i].set(0,0,0);let directionalLength=0;let pointLength=0;let spotLength=0;let rectAreaLength=0;let hemiLength=0;let numDirectionalShadows=0;let numPointShadows=0;let numSpotShadows=0;lights.sort(shadowCastingLightsFirst);for(let i=0,l=lights.length;i0){if(capabilities.isWebGL2){state.rectAreaLTC1=UniformsLib.LTC_FLOAT_1;state.rectAreaLTC2=UniformsLib.LTC_FLOAT_2}else{if(extensions.has("OES_texture_float_linear")===true){state.rectAreaLTC1=UniformsLib.LTC_FLOAT_1;state.rectAreaLTC2=UniformsLib.LTC_FLOAT_2}else if(extensions.has("OES_texture_half_float_linear")===true){state.rectAreaLTC1=UniformsLib.LTC_HALF_1;state.rectAreaLTC2=UniformsLib.LTC_HALF_2}else{console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.")}}}state.ambient[0]=r;state.ambient[1]=g;state.ambient[2]=b;const hash=state.hash;if(hash.directionalLength!==directionalLength||hash.pointLength!==pointLength||hash.spotLength!==spotLength||hash.rectAreaLength!==rectAreaLength||hash.hemiLength!==hemiLength||hash.numDirectionalShadows!==numDirectionalShadows||hash.numPointShadows!==numPointShadows||hash.numSpotShadows!==numSpotShadows){state.directional.length=directionalLength;state.spot.length=spotLength;state.rectArea.length=rectAreaLength;state.point.length=pointLength;state.hemi.length=hemiLength;state.directionalShadow.length=numDirectionalShadows;state.directionalShadowMap.length=numDirectionalShadows;state.pointShadow.length=numPointShadows;state.pointShadowMap.length=numPointShadows;state.spotShadow.length=numSpotShadows;state.spotShadowMap.length=numSpotShadows;state.directionalShadowMatrix.length=numDirectionalShadows;state.pointShadowMatrix.length=numPointShadows;state.spotShadowMatrix.length=numSpotShadows;hash.directionalLength=directionalLength;hash.pointLength=pointLength;hash.spotLength=spotLength;hash.rectAreaLength=rectAreaLength;hash.hemiLength=hemiLength;hash.numDirectionalShadows=numDirectionalShadows;hash.numPointShadows=numPointShadows;hash.numSpotShadows=numSpotShadows;state.version=nextVersion++}}function setupView(lights,camera){let directionalLength=0;let pointLength=0;let spotLength=0;let rectAreaLength=0;let hemiLength=0;const viewMatrix=camera.matrixWorldInverse;for(let i=0,l=lights.length;i=renderStates.get(scene).length){renderState=new WebGLRenderState(extensions,capabilities);renderStates.get(scene).push(renderState)}else{renderState=renderStates.get(scene)[renderCallDepth]}}return renderState}function dispose(){renderStates=new WeakMap}return{get:get,dispose:dispose}}class MeshDepthMaterial extends Material{constructor(parameters){super();this.type="MeshDepthMaterial";this.depthPacking=BasicDepthPacking;this.skinning=false;this.morphTargets=false;this.map=null;this.alphaMap=null;this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.wireframe=false;this.wireframeLinewidth=1;this.fog=false;this.setValues(parameters)}copy(source){super.copy(source);this.depthPacking=source.depthPacking;this.skinning=source.skinning;this.morphTargets=source.morphTargets;this.map=source.map;this.alphaMap=source.alphaMap;this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;return this}}MeshDepthMaterial.prototype.isMeshDepthMaterial=true;class MeshDistanceMaterial extends Material{constructor(parameters){super();this.type="MeshDistanceMaterial";this.referencePosition=new Vector3;this.nearDistance=1;this.farDistance=1e3;this.skinning=false;this.morphTargets=false;this.map=null;this.alphaMap=null;this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.fog=false;this.setValues(parameters)}copy(source){super.copy(source);this.referencePosition.copy(source.referencePosition);this.nearDistance=source.nearDistance;this.farDistance=source.farDistance;this.skinning=source.skinning;this.morphTargets=source.morphTargets;this.map=source.map;this.alphaMap=source.alphaMap;this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;return this}}MeshDistanceMaterial.prototype.isMeshDistanceMaterial=true;var vsm_frag="uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}";var vsm_vert="void main() {\n\tgl_Position = vec4( position, 1.0 );\n}";function WebGLShadowMap(_renderer,_objects,_capabilities){let _frustum=new Frustum;const _shadowMapSize=new Vector2,_viewportSize=new Vector2,_viewport=new Vector4,_depthMaterials=[],_distanceMaterials=[],_materialCache={},_maxTextureSize=_capabilities.maxTextureSize;const shadowSide={0:BackSide,1:FrontSide,2:DoubleSide};const shadowMaterialVertical=new ShaderMaterial({defines:{SAMPLE_RATE:2/8,HALF_SAMPLE_RATE:1/8},uniforms:{shadow_pass:{value:null},resolution:{value:new Vector2},radius:{value:4}},vertexShader:vsm_vert,fragmentShader:vsm_frag});const shadowMaterialHorizontal=shadowMaterialVertical.clone();shadowMaterialHorizontal.defines.HORIZONTAL_PASS=1;const fullScreenTri=new BufferGeometry;fullScreenTri.setAttribute("position",new BufferAttribute(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const fullScreenMesh=new Mesh(fullScreenTri,shadowMaterialVertical);const scope=this;this.enabled=false;this.autoUpdate=true;this.needsUpdate=false;this.type=PCFShadowMap;this.render=function(lights,scene,camera){if(scope.enabled===false)return;if(scope.autoUpdate===false&&scope.needsUpdate===false)return;if(lights.length===0)return;const currentRenderTarget=_renderer.getRenderTarget();const activeCubeFace=_renderer.getActiveCubeFace();const activeMipmapLevel=_renderer.getActiveMipmapLevel();const _state=_renderer.state;_state.setBlending(NoBlending);_state.buffers.color.setClear(1,1,1,1);_state.buffers.depth.setTest(true);_state.setScissorTest(false);for(let i=0,il=lights.length;i_maxTextureSize||_shadowMapSize.y>_maxTextureSize){if(_shadowMapSize.x>_maxTextureSize){_viewportSize.x=Math.floor(_maxTextureSize/shadowFrameExtents.x);_shadowMapSize.x=_viewportSize.x*shadowFrameExtents.x;shadow.mapSize.x=_viewportSize.x}if(_shadowMapSize.y>_maxTextureSize){_viewportSize.y=Math.floor(_maxTextureSize/shadowFrameExtents.y);_shadowMapSize.y=_viewportSize.y*shadowFrameExtents.y;shadow.mapSize.y=_viewportSize.y}}if(shadow.map===null&&!shadow.isPointLightShadow&&this.type===VSMShadowMap){const pars={minFilter:LinearFilter,magFilter:LinearFilter,format:RGBAFormat};shadow.map=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y,pars);shadow.map.texture.name=light.name+".shadowMap";shadow.mapPass=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y,pars);shadow.camera.updateProjectionMatrix()}if(shadow.map===null){const pars={minFilter:NearestFilter,magFilter:NearestFilter,format:RGBAFormat};shadow.map=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y,pars);shadow.map.texture.name=light.name+".shadowMap";shadow.camera.updateProjectionMatrix()}_renderer.setRenderTarget(shadow.map);_renderer.clear();const viewportCount=shadow.getViewportCount();for(let vp=0;vp0}let useSkinning=false;if(object.isSkinnedMesh===true){if(material.skinning===true){useSkinning=true}else{console.warn("THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:",object)}}const useInstancing=object.isInstancedMesh===true;result=getMaterialVariant(useMorphing,useSkinning,useInstancing)}else{result=customMaterial}if(_renderer.localClippingEnabled&&material.clipShadows===true&&material.clippingPlanes.length!==0){const keyA=result.uuid,keyB=material.uuid;let materialsForVariant=_materialCache[keyA];if(materialsForVariant===undefined){materialsForVariant={};_materialCache[keyA]=materialsForVariant}let cachedMaterial=materialsForVariant[keyB];if(cachedMaterial===undefined){cachedMaterial=result.clone();materialsForVariant[keyB]=cachedMaterial}result=cachedMaterial}result.visible=material.visible;result.wireframe=material.wireframe;if(type===VSMShadowMap){result.side=material.shadowSide!==null?material.shadowSide:material.side}else{result.side=material.shadowSide!==null?material.shadowSide:shadowSide[material.side]}result.clipShadows=material.clipShadows;result.clippingPlanes=material.clippingPlanes;result.clipIntersection=material.clipIntersection;result.wireframeLinewidth=material.wireframeLinewidth;result.linewidth=material.linewidth;if(light.isPointLight===true&&result.isMeshDistanceMaterial===true){result.referencePosition.setFromMatrixPosition(light.matrixWorld);result.nearDistance=shadowCameraNear;result.farDistance=shadowCameraFar}return result}function renderObject(object,camera,shadowCamera,light,type){if(object.visible===false)return;const visible=object.layers.test(camera.layers);if(visible&&(object.isMesh||object.isLine||object.isPoints)){if((object.castShadow||object.receiveShadow&&type===VSMShadowMap)&&(!object.frustumCulled||_frustum.intersectsObject(object))){object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse,object.matrixWorld);const geometry=_objects.update(object);const material=object.material;if(Array.isArray(material)){const groups=geometry.groups;for(let k=0,kl=groups.length;k=1}else if(glVersion.indexOf("OpenGL ES")!==-1){version=parseFloat(/^OpenGL ES (\d)/.exec(glVersion)[1]);lineWidthAvailable=version>=2}let currentTextureSlot=null;let currentBoundTextures={};const currentScissor=new Vector4(0,0,gl.canvas.width,gl.canvas.height);const currentViewport=new Vector4(0,0,gl.canvas.width,gl.canvas.height);function createTexture(type,target,count){const data=new Uint8Array(4);const texture=gl.createTexture();gl.bindTexture(type,texture);gl.texParameteri(type,10241,9728);gl.texParameteri(type,10240,9728);for(let i=0;imaxSize||image.height>maxSize){scale=maxSize/Math.max(image.width,image.height)}if(scale<1||needsPowerOfTwo===true){if(typeof HTMLImageElement!=="undefined"&&image instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&image instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&image instanceof ImageBitmap){const floor=needsPowerOfTwo?floorPowerOfTwo:Math.floor;const width=floor(scale*image.width);const height=floor(scale*image.height);if(_canvas===undefined)_canvas=createCanvas(width,height);const canvas=needsNewCanvas?createCanvas(width,height):_canvas;canvas.width=width;canvas.height=height;const context=canvas.getContext("2d");context.drawImage(image,0,0,width,height);console.warn("THREE.WebGLRenderer: Texture has been resized from ("+image.width+"x"+image.height+") to ("+width+"x"+height+").");return canvas}else{if("data"in image){console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+image.width+"x"+image.height+").")}return image}}return image}function isPowerOfTwo$1(image){return isPowerOfTwo(image.width)&&isPowerOfTwo(image.height)}function textureNeedsPowerOfTwo(texture){if(isWebGL2)return false;return texture.wrapS!==ClampToEdgeWrapping||texture.wrapT!==ClampToEdgeWrapping||texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter}function textureNeedsGenerateMipmaps(texture,supportsMips){return texture.generateMipmaps&&supportsMips&&texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter}function generateMipmap(target,texture,width,height){_gl.generateMipmap(target);const textureProperties=properties.get(texture);textureProperties.__maxMipLevel=Math.log2(Math.max(width,height))}function getInternalFormat(internalFormatName,glFormat,glType){if(isWebGL2===false)return glFormat;if(internalFormatName!==null){if(_gl[internalFormatName]!==undefined)return _gl[internalFormatName];console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format '"+internalFormatName+"'")}let internalFormat=glFormat;if(glFormat===6403){if(glType===5126)internalFormat=33326;if(glType===5131)internalFormat=33325;if(glType===5121)internalFormat=33321}if(glFormat===6407){if(glType===5126)internalFormat=34837;if(glType===5131)internalFormat=34843;if(glType===5121)internalFormat=32849}if(glFormat===6408){if(glType===5126)internalFormat=34836;if(glType===5131)internalFormat=34842;if(glType===5121)internalFormat=32856}if(internalFormat===33325||internalFormat===33326||internalFormat===34842||internalFormat===34836){extensions.get("EXT_color_buffer_float")}return internalFormat}function filterFallback(f){if(f===NearestFilter||f===NearestMipmapNearestFilter||f===NearestMipmapLinearFilter){return 9728}return 9729}function onTextureDispose(event){const texture=event.target;texture.removeEventListener("dispose",onTextureDispose);deallocateTexture(texture);if(texture.isVideoTexture){_videoTextures.delete(texture)}info.memory.textures--}function onRenderTargetDispose(event){const renderTarget=event.target;renderTarget.removeEventListener("dispose",onRenderTargetDispose);deallocateRenderTarget(renderTarget);info.memory.textures--}function deallocateTexture(texture){const textureProperties=properties.get(texture);if(textureProperties.__webglInit===undefined)return;_gl.deleteTexture(textureProperties.__webglTexture);properties.remove(texture)}function deallocateRenderTarget(renderTarget){const texture=renderTarget.texture;const renderTargetProperties=properties.get(renderTarget);const textureProperties=properties.get(texture);if(!renderTarget)return;if(textureProperties.__webglTexture!==undefined){_gl.deleteTexture(textureProperties.__webglTexture)}if(renderTarget.depthTexture){renderTarget.depthTexture.dispose()}if(renderTarget.isWebGLCubeRenderTarget){for(let i=0;i<6;i++){_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]);if(renderTargetProperties.__webglDepthbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i])}}else{_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer);if(renderTargetProperties.__webglDepthbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer);if(renderTargetProperties.__webglMultisampledFramebuffer)_gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer);if(renderTargetProperties.__webglColorRenderbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer);if(renderTargetProperties.__webglDepthRenderbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer)}properties.remove(texture);properties.remove(renderTarget)}let textureUnits=0;function resetTextureUnits(){textureUnits=0}function allocateTextureUnit(){const textureUnit=textureUnits;if(textureUnit>=maxTextures){console.warn("THREE.WebGLTextures: Trying to use "+textureUnit+" texture units while this GPU supports only "+maxTextures)}textureUnits+=1;return textureUnit}function setTexture2D(texture,slot){const textureProperties=properties.get(texture);if(texture.isVideoTexture)updateVideoTexture(texture);if(texture.version>0&&textureProperties.__version!==texture.version){const image=texture.image;if(image===undefined){console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined")}else if(image.complete===false){console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete")}else{uploadTexture(textureProperties,texture,slot);return}}state.activeTexture(33984+slot);state.bindTexture(3553,textureProperties.__webglTexture)}function setTexture2DArray(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(35866,textureProperties.__webglTexture)}function setTexture3D(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(32879,textureProperties.__webglTexture)}function setTextureCube(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadCubeTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(34067,textureProperties.__webglTexture)}const wrappingToGL={[RepeatWrapping]:10497,[ClampToEdgeWrapping]:33071,[MirroredRepeatWrapping]:33648};const filterToGL={[NearestFilter]:9728,[NearestMipmapNearestFilter]:9984,[NearestMipmapLinearFilter]:9986,[LinearFilter]:9729,[LinearMipmapNearestFilter]:9985,[LinearMipmapLinearFilter]:9987};function setTextureParameters(textureType,texture,supportsMips){if(supportsMips){_gl.texParameteri(textureType,10242,wrappingToGL[texture.wrapS]);_gl.texParameteri(textureType,10243,wrappingToGL[texture.wrapT]);if(textureType===32879||textureType===35866){_gl.texParameteri(textureType,32882,wrappingToGL[texture.wrapR])}_gl.texParameteri(textureType,10240,filterToGL[texture.magFilter]);_gl.texParameteri(textureType,10241,filterToGL[texture.minFilter])}else{_gl.texParameteri(textureType,10242,33071);_gl.texParameteri(textureType,10243,33071);if(textureType===32879||textureType===35866){_gl.texParameteri(textureType,32882,33071)}if(texture.wrapS!==ClampToEdgeWrapping||texture.wrapT!==ClampToEdgeWrapping){console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.")}_gl.texParameteri(textureType,10240,filterFallback(texture.magFilter));_gl.texParameteri(textureType,10241,filterFallback(texture.minFilter));if(texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter){console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.")}}if(extensions.has("EXT_texture_filter_anisotropic")===true){const extension=extensions.get("EXT_texture_filter_anisotropic");if(texture.type===FloatType&&extensions.has("OES_texture_float_linear")===false)return;if(isWebGL2===false&&(texture.type===HalfFloatType&&extensions.has("OES_texture_half_float_linear")===false))return;if(texture.anisotropy>1||properties.get(texture).__currentAnisotropy){_gl.texParameterf(textureType,extension.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(texture.anisotropy,capabilities.getMaxAnisotropy()));properties.get(texture).__currentAnisotropy=texture.anisotropy}}}function initTexture(textureProperties,texture){if(textureProperties.__webglInit===undefined){textureProperties.__webglInit=true;texture.addEventListener("dispose",onTextureDispose);textureProperties.__webglTexture=_gl.createTexture();info.memory.textures++}}function uploadTexture(textureProperties,texture,slot){let textureType=3553;if(texture.isDataTexture2DArray)textureType=35866;if(texture.isDataTexture3D)textureType=32879;initTexture(textureProperties,texture);state.activeTexture(33984+slot);state.bindTexture(textureType,textureProperties.__webglTexture);_gl.pixelStorei(37440,texture.flipY);_gl.pixelStorei(37441,texture.premultiplyAlpha);_gl.pixelStorei(3317,texture.unpackAlignment);_gl.pixelStorei(37443,0);const needsPowerOfTwo=textureNeedsPowerOfTwo(texture)&&isPowerOfTwo$1(texture.image)===false;const image=resizeImage(texture.image,needsPowerOfTwo,false,maxTextureSize);const supportsMips=isPowerOfTwo$1(image)||isWebGL2,glFormat=utils.convert(texture.format);let glType=utils.convert(texture.type),glInternalFormat=getInternalFormat(texture.internalFormat,glFormat,glType);setTextureParameters(textureType,texture,supportsMips);let mipmap;const mipmaps=texture.mipmaps;if(texture.isDepthTexture){glInternalFormat=6402;if(isWebGL2){if(texture.type===FloatType){glInternalFormat=36012}else if(texture.type===UnsignedIntType){glInternalFormat=33190}else if(texture.type===UnsignedInt248Type){glInternalFormat=35056}else{glInternalFormat=33189}}else{if(texture.type===FloatType){console.error("WebGLRenderer: Floating point depth texture requires WebGL2.")}}if(texture.format===DepthFormat&&glInternalFormat===6402){if(texture.type!==UnsignedShortType&&texture.type!==UnsignedIntType){console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.");texture.type=UnsignedShortType;glType=utils.convert(texture.type)}}if(texture.format===DepthStencilFormat&&glInternalFormat===6402){glInternalFormat=34041;if(texture.type!==UnsignedInt248Type){console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.");texture.type=UnsignedInt248Type;glType=utils.convert(texture.type)}}state.texImage2D(3553,0,glInternalFormat,image.width,image.height,0,glFormat,glType,null)}else if(texture.isDataTexture){if(mipmaps.length>0&&supportsMips){for(let i=0,il=mipmaps.length;i0&&supportsMips){for(let i=0,il=mipmaps.length;idistanceToPinch+threshold){hand.inputState.pinching=false;this.dispatchEvent({type:"pinchend",handedness:inputSource.handedness,target:this})}else if(!hand.inputState.pinching&&distance<=distanceToPinch-threshold){hand.inputState.pinching=true;this.dispatchEvent({type:"pinchstart",handedness:inputSource.handedness,target:this})}}else{if(grip!==null&&inputSource.gripSpace){gripPose=frame.getPose(inputSource.gripSpace,referenceSpace);if(gripPose!==null){grip.matrix.fromArray(gripPose.transform.matrix);grip.matrix.decompose(grip.position,grip.rotation,grip.scale);if(gripPose.linearVelocity){grip.hasLinearVelocity=true;grip.linearVelocity.copy(gripPose.linearVelocity)}else{grip.hasLinearVelocity=false}if(gripPose.angularVelocity){grip.hasAngularVelocity=true;grip.angularVelocity.copy(gripPose.angularVelocity)}else{grip.hasAngularVelocity=false}}}}}if(targetRay!==null){targetRay.visible=inputPose!==null}if(grip!==null){grip.visible=gripPose!==null}if(hand!==null){hand.visible=handPose!==null}return this}}class WebXRManager extends EventDispatcher$1{constructor(renderer,gl){super();const scope=this;const state=renderer.state;let session=null;let framebufferScaleFactor=1;let referenceSpace=null;let referenceSpaceType="local-floor";let pose=null;const controllers=[];const inputSourcesMap=new Map;const cameraL=new PerspectiveCamera;cameraL.layers.enable(1);cameraL.viewport=new Vector4;const cameraR=new PerspectiveCamera;cameraR.layers.enable(2);cameraR.viewport=new Vector4;const cameras=[cameraL,cameraR];const cameraVR=new ArrayCamera;cameraVR.layers.enable(1);cameraVR.layers.enable(2);let _currentDepthNear=null;let _currentDepthFar=null;this.enabled=false;this.isPresenting=false;this.getController=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getTargetRaySpace()};this.getControllerGrip=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getGripSpace()};this.getHand=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getHandSpace()};function onSessionEvent(event){const controller=inputSourcesMap.get(event.inputSource);if(controller){controller.dispatchEvent({type:event.type,data:event.inputSource})}}function onSessionEnd(){inputSourcesMap.forEach((function(controller,inputSource){controller.disconnect(inputSource)}));inputSourcesMap.clear();_currentDepthNear=null;_currentDepthFar=null;state.bindXRFramebuffer(null);renderer.setRenderTarget(renderer.getRenderTarget());animation.stop();scope.isPresenting=false;scope.dispatchEvent({type:"sessionend"})}this.setFramebufferScaleFactor=function(value){framebufferScaleFactor=value;if(scope.isPresenting===true){console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting.")}};this.setReferenceSpaceType=function(value){referenceSpaceType=value;if(scope.isPresenting===true){console.warn("THREE.WebXRManager: Cannot change reference space type while presenting.")}};this.getReferenceSpace=function(){return referenceSpace};this.getSession=function(){return session};this.setSession=async function(value){session=value;if(session!==null){session.addEventListener("select",onSessionEvent);session.addEventListener("selectstart",onSessionEvent);session.addEventListener("selectend",onSessionEvent);session.addEventListener("squeeze",onSessionEvent);session.addEventListener("squeezestart",onSessionEvent);session.addEventListener("squeezeend",onSessionEvent);session.addEventListener("end",onSessionEnd);session.addEventListener("inputsourceschange",onInputSourcesChange);const attributes=gl.getContextAttributes();if(attributes.xrCompatible!==true){await gl.makeXRCompatible()}const layerInit={antialias:attributes.antialias,alpha:attributes.alpha,depth:attributes.depth,stencil:attributes.stencil,framebufferScaleFactor:framebufferScaleFactor};const baseLayer=new XRWebGLLayer(session,gl,layerInit);session.updateRenderState({baseLayer:baseLayer});referenceSpace=await session.requestReferenceSpace(referenceSpaceType);animation.setContext(session);animation.start();scope.isPresenting=true;scope.dispatchEvent({type:"sessionstart"})}};function onInputSourcesChange(event){const inputSources=session.inputSources;for(let i=0;i0)renderObjects(opaqueObjects,scene,camera);if(transparentObjects.length>0)renderObjects(transparentObjects,scene,camera);if(_currentRenderTarget!==null){textures.updateRenderTargetMipmap(_currentRenderTarget);textures.updateMultisampleRenderTarget(_currentRenderTarget)}if(scene.isScene===true)scene.onAfterRender(_this,scene,camera);state.buffers.depth.setTest(true);state.buffers.depth.setMask(true);state.buffers.color.setMask(true);state.setPolygonOffset(false);bindingStates.resetDefaultState();_currentMaterialId=-1;_currentCamera=null;renderStateStack.pop();if(renderStateStack.length>0){currentRenderState=renderStateStack[renderStateStack.length-1]}else{currentRenderState=null}renderListStack.pop();if(renderListStack.length>0){currentRenderList=renderListStack[renderListStack.length-1]}else{currentRenderList=null}};function projectObject(object,camera,groupOrder,sortObjects){if(object.visible===false)return;const visible=object.layers.test(camera.layers);if(visible){if(object.isGroup){groupOrder=object.renderOrder}else if(object.isLOD){if(object.autoUpdate===true)object.update(camera)}else if(object.isLight){currentRenderState.pushLight(object);if(object.castShadow){currentRenderState.pushShadow(object)}}else if(object.isSprite){if(!object.frustumCulled||_frustum.intersectsSprite(object)){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix)}const geometry=objects.update(object);const material=object.material;if(material.visible){currentRenderList.push(object,geometry,material,groupOrder,_vector3.z,null)}}}else if(object.isImmediateRenderObject){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix)}currentRenderList.push(object,null,object.material,groupOrder,_vector3.z,null)}else if(object.isMesh||object.isLine||object.isPoints){if(object.isSkinnedMesh){if(object.skeleton.frame!==info.render.frame){object.skeleton.update();object.skeleton.frame=info.render.frame}}if(!object.frustumCulled||_frustum.intersectsObject(object)){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix)}const geometry=objects.update(object);const material=object.material;if(Array.isArray(material)){const groups=geometry.groups;for(let i=0,l=groups.length;i=0&&x<=renderTarget.width-width&&(y>=0&&y<=renderTarget.height-height)){_gl.readPixels(x,y,width,height,utils.convert(textureFormat),utils.convert(textureType),buffer)}}else{console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}}finally{const framebuffer=_currentRenderTarget!==null?properties.get(_currentRenderTarget).__webglFramebuffer:null;state.bindFramebuffer(36160,framebuffer)}}};this.copyFramebufferToTexture=function(position,texture,level=0){const levelScale=Math.pow(2,-level);const width=Math.floor(texture.image.width*levelScale);const height=Math.floor(texture.image.height*levelScale);const glFormat=utils.convert(texture.format);textures.setTexture2D(texture,0);_gl.copyTexImage2D(3553,level,glFormat,position.x,position.y,width,height,0);state.unbindTexture()};this.copyTextureToTexture=function(position,srcTexture,dstTexture,level=0){const width=srcTexture.image.width;const height=srcTexture.image.height;const glFormat=utils.convert(dstTexture.format);const glType=utils.convert(dstTexture.type);textures.setTexture2D(dstTexture,0);_gl.pixelStorei(37440,dstTexture.flipY);_gl.pixelStorei(37441,dstTexture.premultiplyAlpha);_gl.pixelStorei(3317,dstTexture.unpackAlignment);if(srcTexture.isDataTexture){_gl.texSubImage2D(3553,level,position.x,position.y,width,height,glFormat,glType,srcTexture.image.data)}else{if(srcTexture.isCompressedTexture){_gl.compressedTexSubImage2D(3553,level,position.x,position.y,srcTexture.mipmaps[0].width,srcTexture.mipmaps[0].height,glFormat,srcTexture.mipmaps[0].data)}else{_gl.texSubImage2D(3553,level,position.x,position.y,glFormat,glType,srcTexture.image)}}if(level===0&&dstTexture.generateMipmaps)_gl.generateMipmap(3553);state.unbindTexture()};this.copyTextureToTexture3D=function(sourceBox,position,srcTexture,dstTexture,level=0){if(_this.isWebGL1Renderer){console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.");return}const{width:width,height:height,data:data}=srcTexture.image;const glFormat=utils.convert(dstTexture.format);const glType=utils.convert(dstTexture.type);let glTarget;if(dstTexture.isDataTexture3D){textures.setTexture3D(dstTexture,0);glTarget=32879}else if(dstTexture.isDataTexture2DArray){textures.setTexture2DArray(dstTexture,0);glTarget=35866}else{console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.");return}_gl.pixelStorei(37440,dstTexture.flipY);_gl.pixelStorei(37441,dstTexture.premultiplyAlpha);_gl.pixelStorei(3317,dstTexture.unpackAlignment);const unpackRowLen=_gl.getParameter(3314);const unpackImageHeight=_gl.getParameter(32878);const unpackSkipPixels=_gl.getParameter(3316);const unpackSkipRows=_gl.getParameter(3315);const unpackSkipImages=_gl.getParameter(32877);_gl.pixelStorei(3314,width);_gl.pixelStorei(32878,height);_gl.pixelStorei(3316,sourceBox.min.x);_gl.pixelStorei(3315,sourceBox.min.y);_gl.pixelStorei(32877,sourceBox.min.z);_gl.texSubImage3D(glTarget,level,position.x,position.y,position.z,sourceBox.max.x-sourceBox.min.x+1,sourceBox.max.y-sourceBox.min.y+1,sourceBox.max.z-sourceBox.min.z+1,glFormat,glType,data);_gl.pixelStorei(3314,unpackRowLen);_gl.pixelStorei(32878,unpackImageHeight);_gl.pixelStorei(3316,unpackSkipPixels);_gl.pixelStorei(3315,unpackSkipRows);_gl.pixelStorei(32877,unpackSkipImages);if(level===0&&dstTexture.generateMipmaps)_gl.generateMipmap(glTarget);state.unbindTexture()};this.initTexture=function(texture){textures.setTexture2D(texture,0);state.unbindTexture()};this.resetState=function(){_currentActiveCubeFace=0;_currentActiveMipmapLevel=0;_currentRenderTarget=null;state.reset();bindingStates.reset()};if(typeof __THREE_DEVTOOLS__!=="undefined"){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}}class WebGL1Renderer extends WebGLRenderer{}WebGL1Renderer.prototype.isWebGL1Renderer=true;class Scene extends Object3D{constructor(){super();this.type="Scene";this.background=null;this.environment=null;this.fog=null;this.overrideMaterial=null;this.autoUpdate=true;if(typeof __THREE_DEVTOOLS__!=="undefined"){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}}copy(source,recursive){super.copy(source,recursive);if(source.background!==null)this.background=source.background.clone();if(source.environment!==null)this.environment=source.environment.clone();if(source.fog!==null)this.fog=source.fog.clone();if(source.overrideMaterial!==null)this.overrideMaterial=source.overrideMaterial.clone();this.autoUpdate=source.autoUpdate;this.matrixAutoUpdate=source.matrixAutoUpdate;return this}toJSON(meta){const data=super.toJSON(meta);if(this.background!==null)data.object.background=this.background.toJSON(meta);if(this.environment!==null)data.object.environment=this.environment.toJSON(meta);if(this.fog!==null)data.object.fog=this.fog.toJSON();return data}}Scene.prototype.isScene=true;class InterleavedBuffer{constructor(array,stride){this.array=array;this.stride=stride;this.count=array!==undefined?array.length/stride:0;this.usage=StaticDrawUsage;this.updateRange={offset:0,count:-1};this.version=0;this.uuid=generateUUID();this.onUploadCallback=function(){}}set needsUpdate(value){if(value===true)this.version++}setUsage(value){this.usage=value;return this}copy(source){this.array=new source.array.constructor(source.array);this.count=source.count;this.stride=source.stride;this.usage=source.usage;return this}copyAt(index1,attribute,index2){index1*=this.stride;index2*=attribute.stride;for(let i=0,l=this.stride;iraycaster.far)return;intersects.push({distance:distance,point:_intersectPoint.clone(),uv:Triangle.getUV(_intersectPoint,_vA,_vB,_vC,_uvA,_uvB,_uvC,new Vector2),face:null,object:this})}copy(source){super.copy(source);if(source.center!==undefined)this.center.copy(source.center);this.material=source.material;return this}}Sprite.prototype.isSprite=true;function transformVertex(vertexPosition,mvPosition,center,scale,sin,cos){_alignedPosition.subVectors(vertexPosition,center).addScalar(.5).multiply(scale);if(sin!==undefined){_rotatedPosition.x=cos*_alignedPosition.x-sin*_alignedPosition.y;_rotatedPosition.y=sin*_alignedPosition.x+cos*_alignedPosition.y}else{_rotatedPosition.copy(_alignedPosition)}vertexPosition.copy(mvPosition);vertexPosition.x+=_rotatedPosition.x;vertexPosition.y+=_rotatedPosition.y;vertexPosition.applyMatrix4(_viewWorldMatrix)}const _basePosition=new Vector3;const _skinIndex=new Vector4;const _skinWeight=new Vector4;const _vector$5=new Vector3;const _matrix=new Matrix4;class SkinnedMesh extends Mesh{constructor(geometry,material){super(geometry,material);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new Matrix4;this.bindMatrixInverse=new Matrix4}copy(source){super.copy(source);this.bindMode=source.bindMode;this.bindMatrix.copy(source.bindMatrix);this.bindMatrixInverse.copy(source.bindMatrixInverse);this.skeleton=source.skeleton;return this}bind(skeleton,bindMatrix){this.skeleton=skeleton;if(bindMatrix===undefined){this.updateMatrixWorld(true);this.skeleton.calculateInverses();bindMatrix=this.matrixWorld}this.bindMatrix.copy(bindMatrix);this.bindMatrixInverse.copy(bindMatrix).invert()}pose(){this.skeleton.pose()}normalizeSkinWeights(){const vector=new Vector4;const skinWeight=this.geometry.attributes.skinWeight;for(let i=0,l=skinWeight.count;ilocalThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);const distance=raycaster.ray.origin.distanceTo(interRay);if(distanceraycaster.far)continue;intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}else{const start=Math.max(0,drawRange.start);const end=Math.min(positionAttribute.count,drawRange.start+drawRange.count);for(let i=start,l=end-1;ilocalThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);const distance=raycaster.ray.origin.distanceTo(interRay);if(distanceraycaster.far)continue;intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}else if(geometry.isGeometry){console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}}updateMorphTargets(){const geometry=this.geometry;if(geometry.isBufferGeometry){const morphAttributes=geometry.morphAttributes;const keys=Object.keys(morphAttributes);if(keys.length>0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}}Line.prototype.isLine=true;const _start=new Vector3;const _end=new Vector3;class LineSegments extends Line{constructor(geometry,material){super(geometry,material);this.type="LineSegments"}computeLineDistances(){const geometry=this.geometry;if(geometry.isBufferGeometry){if(geometry.index===null){const positionAttribute=geometry.attributes.position;const lineDistances=[];for(let i=0,l=positionAttribute.count;i0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}}Points.prototype.isPoints=true;function testPoint(point,index,localThresholdSq,matrixWorld,raycaster,intersects,object){const rayPointDistanceSq=_ray.distanceSqToPoint(point);if(rayPointDistanceSqraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint,index:index,face:null,object:object})}}class VideoTexture extends Texture{constructor(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){super(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.format=format!==undefined?format:RGBFormat;this.minFilter=minFilter!==undefined?minFilter:LinearFilter;this.magFilter=magFilter!==undefined?magFilter:LinearFilter;this.generateMipmaps=false;const scope=this;function updateVideo(){scope.needsUpdate=true;video.requestVideoFrameCallback(updateVideo)}if("requestVideoFrameCallback"in video){video.requestVideoFrameCallback(updateVideo)}}clone(){return new this.constructor(this.image).copy(this)}update(){const video=this.image;const hasVideoFrameCallback="requestVideoFrameCallback"in video;if(hasVideoFrameCallback===false&&video.readyState>=video.HAVE_CURRENT_DATA){this.needsUpdate=true}}}VideoTexture.prototype.isVideoTexture=true;class CompressedTexture extends Texture{constructor(mipmaps,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy,encoding){super(null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this.image={width:width,height:height};this.mipmaps=mipmaps;this.flipY=false;this.generateMipmaps=false}}CompressedTexture.prototype.isCompressedTexture=true;class CanvasTexture extends Texture{constructor(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){super(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.needsUpdate=true}}CanvasTexture.prototype.isCanvasTexture=true;class DepthTexture extends Texture{constructor(width,height,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy,format){format=format!==undefined?format:DepthFormat;if(format!==DepthFormat&&format!==DepthStencilFormat){throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat")}if(type===undefined&&format===DepthFormat)type=UnsignedShortType;if(type===undefined&&format===DepthStencilFormat)type=UnsignedInt248Type;super(null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.image={width:width,height:height};this.magFilter=magFilter!==undefined?magFilter:NearestFilter;this.minFilter=minFilter!==undefined?minFilter:NearestFilter;this.flipY=false;this.generateMipmaps=false}}DepthTexture.prototype.isDepthTexture=true;new Vector3;new Vector3;new Vector3;new Triangle;const Earcut={triangulate:function(data,holeIndices,dim){dim=dim||2;const hasHoles=holeIndices&&holeIndices.length;const outerLen=hasHoles?holeIndices[0]*dim:data.length;let outerNode=linkedList(data,0,outerLen,dim,true);const triangles=[];if(!outerNode||outerNode.next===outerNode.prev)return triangles;let minX,minY,maxX,maxY,x,y,invSize;if(hasHoles)outerNode=eliminateHoles(data,holeIndices,outerNode,dim);if(data.length>80*dim){minX=maxX=data[0];minY=maxY=data[1];for(let i=dim;imaxX)maxX=x;if(y>maxY)maxY=y}invSize=Math.max(maxX-minX,maxY-minY);invSize=invSize!==0?1/invSize:0}earcutLinked(outerNode,triangles,dim,minX,minY,invSize);return triangles}};function linkedList(data,start,end,dim,clockwise){let i,last;if(clockwise===signedArea(data,start,end,dim)>0){for(i=start;i=start;i-=dim)last=insertNode(i,data[i],data[i+1],last)}if(last&&equals(last,last.next)){removeNode(last);last=last.next}return last}function filterPoints(start,end){if(!start)return start;if(!end)end=start;let p=start,again;do{again=false;if(!p.steiner&&(equals(p,p.next)||area(p.prev,p,p.next)===0)){removeNode(p);p=end=p.prev;if(p===p.next)break;again=true}else{p=p.next}}while(again||p!==end);return end}function earcutLinked(ear,triangles,dim,minX,minY,invSize,pass){if(!ear)return;if(!pass&&invSize)indexCurve(ear,minX,minY,invSize);let stop=ear,prev,next;while(ear.prev!==ear.next){prev=ear.prev;next=ear.next;if(invSize?isEarHashed(ear,minX,minY,invSize):isEar(ear)){triangles.push(prev.i/dim);triangles.push(ear.i/dim);triangles.push(next.i/dim);removeNode(ear);ear=next.next;stop=next.next;continue}ear=next;if(ear===stop){if(!pass){earcutLinked(filterPoints(ear),triangles,dim,minX,minY,invSize,1)}else if(pass===1){ear=cureLocalIntersections(filterPoints(ear),triangles,dim);earcutLinked(ear,triangles,dim,minX,minY,invSize,2)}else if(pass===2){splitEarcut(ear,triangles,dim,minX,minY,invSize)}break}}}function isEar(ear){const a=ear.prev,b=ear,c=ear.next;if(area(a,b,c)>=0)return false;let p=ear.next.next;while(p!==ear.prev){if(pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.next}return true}function isEarHashed(ear,minX,minY,invSize){const a=ear.prev,b=ear,c=ear.next;if(area(a,b,c)>=0)return false;const minTX=a.xb.x?a.x>c.x?a.x:c.x:b.x>c.x?b.x:c.x,maxTY=a.y>b.y?a.y>c.y?a.y:c.y:b.y>c.y?b.y:c.y;const minZ=zOrder(minTX,minTY,minX,minY,invSize),maxZ=zOrder(maxTX,maxTY,minX,minY,invSize);let p=ear.prevZ,n=ear.nextZ;while(p&&p.z>=minZ&&n&&n.z<=maxZ){if(p!==ear.prev&&p!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.prevZ;if(n!==ear.prev&&n!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,n.x,n.y)&&area(n.prev,n,n.next)>=0)return false;n=n.nextZ}while(p&&p.z>=minZ){if(p!==ear.prev&&p!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.prevZ}while(n&&n.z<=maxZ){if(n!==ear.prev&&n!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,n.x,n.y)&&area(n.prev,n,n.next)>=0)return false;n=n.nextZ}return true}function cureLocalIntersections(start,triangles,dim){let p=start;do{const a=p.prev,b=p.next.next;if(!equals(a,b)&&intersects(a,p,p.next,b)&&locallyInside(a,b)&&locallyInside(b,a)){triangles.push(a.i/dim);triangles.push(p.i/dim);triangles.push(b.i/dim);removeNode(p);removeNode(p.next);p=start=b}p=p.next}while(p!==start);return filterPoints(p)}function splitEarcut(start,triangles,dim,minX,minY,invSize){let a=start;do{let b=a.next.next;while(b!==a.prev){if(a.i!==b.i&&isValidDiagonal(a,b)){let c=splitPolygon(a,b);a=filterPoints(a,a.next);c=filterPoints(c,c.next);earcutLinked(a,triangles,dim,minX,minY,invSize);earcutLinked(c,triangles,dim,minX,minY,invSize);return}b=b.next}a=a.next}while(a!==start)}function eliminateHoles(data,holeIndices,outerNode,dim){const queue=[];let i,len,start,end,list;for(i=0,len=holeIndices.length;i=p.next.y&&p.next.y!==p.y){const x=p.x+(hy-p.y)*(p.next.x-p.x)/(p.next.y-p.y);if(x<=hx&&x>qx){qx=x;if(x===hx){if(hy===p.y)return p;if(hy===p.next.y)return p.next}m=p.x=p.x&&p.x>=mx&&hx!==p.x&&pointInTriangle(hym.x||p.x===m.x&§orContainsSector(m,p)))){m=p;tanMin=tan}}p=p.next}while(p!==stop);return m}function sectorContainsSector(m,p){return area(m.prev,m,p.prev)<0&&area(p.next,m,m.next)<0}function indexCurve(start,minX,minY,invSize){let p=start;do{if(p.z===null)p.z=zOrder(p.x,p.y,minX,minY,invSize);p.prevZ=p.prev;p.nextZ=p.next;p=p.next}while(p!==start);p.prevZ.nextZ=null;p.prevZ=null;sortLinked(p)}function sortLinked(list){let i,p,q,e,tail,numMerges,pSize,qSize,inSize=1;do{p=list;list=null;tail=null;numMerges=0;while(p){numMerges++;q=p;pSize=0;for(i=0;i0||qSize>0&&q){if(pSize!==0&&(qSize===0||!q||p.z<=q.z)){e=p;p=p.nextZ;pSize--}else{e=q;q=q.nextZ;qSize--}if(tail)tail.nextZ=e;else list=e;e.prevZ=tail;tail=e}p=q}tail.nextZ=null;inSize*=2}while(numMerges>1);return list}function zOrder(x,y,minX,minY,invSize){x=32767*(x-minX)*invSize;y=32767*(y-minY)*invSize;x=(x|x<<8)&16711935;x=(x|x<<4)&252645135;x=(x|x<<2)&858993459;x=(x|x<<1)&1431655765;y=(y|y<<8)&16711935;y=(y|y<<4)&252645135;y=(y|y<<2)&858993459;y=(y|y<<1)&1431655765;return x|y<<1}function getLeftmost(start){let p=start,leftmost=start;do{if(p.x=0&&(ax-px)*(by-py)-(bx-px)*(ay-py)>=0&&(bx-px)*(cy-py)-(cx-px)*(by-py)>=0}function isValidDiagonal(a,b){return a.next.i!==b.i&&a.prev.i!==b.i&&!intersectsPolygon(a,b)&&(locallyInside(a,b)&&locallyInside(b,a)&&middleInside(a,b)&&(area(a.prev,a,b.prev)||area(a,b.prev,b))||equals(a,b)&&area(a.prev,a,a.next)>0&&area(b.prev,b,b.next)>0)}function area(p,q,r){return(q.y-p.y)*(r.x-q.x)-(q.x-p.x)*(r.y-q.y)}function equals(p1,p2){return p1.x===p2.x&&p1.y===p2.y}function intersects(p1,q1,p2,q2){const o1=sign(area(p1,q1,p2));const o2=sign(area(p1,q1,q2));const o3=sign(area(p2,q2,p1));const o4=sign(area(p2,q2,q1));if(o1!==o2&&o3!==o4)return true;if(o1===0&&onSegment(p1,p2,q1))return true;if(o2===0&&onSegment(p1,q2,q1))return true;if(o3===0&&onSegment(p2,p1,q2))return true;if(o4===0&&onSegment(p2,q1,q2))return true;return false}function onSegment(p,q,r){return q.x<=Math.max(p.x,r.x)&&q.x>=Math.min(p.x,r.x)&&q.y<=Math.max(p.y,r.y)&&q.y>=Math.min(p.y,r.y)}function sign(num){return num>0?1:num<0?-1:0}function intersectsPolygon(a,b){let p=a;do{if(p.i!==a.i&&p.next.i!==a.i&&p.i!==b.i&&p.next.i!==b.i&&intersects(p,p.next,a,b))return true;p=p.next}while(p!==a);return false}function locallyInside(a,b){return area(a.prev,a,a.next)<0?area(a,b,a.next)>=0&&area(a,a.prev,b)>=0:area(a,b,a.prev)<0||area(a,a.next,b)<0}function middleInside(a,b){let p=a,inside=false;const px=(a.x+b.x)/2,py=(a.y+b.y)/2;do{if(p.y>py!==p.next.y>py&&p.next.y!==p.y&&px<(p.next.x-p.x)*(py-p.y)/(p.next.y-p.y)+p.x)inside=!inside;p=p.next}while(p!==a);return inside}function splitPolygon(a,b){const a2=new Node(a.i,a.x,a.y),b2=new Node(b.i,b.x,b.y),an=a.next,bp=b.prev;a.next=b;b.prev=a;a2.next=an;an.prev=a2;b2.next=a2;a2.prev=b2;bp.next=b2;b2.prev=bp;return b2}function insertNode(i,x,y,last){const p=new Node(i,x,y);if(!last){p.prev=p;p.next=p}else{p.next=last.next;p.prev=last;last.next.prev=p;last.next=p}return p}function removeNode(p){p.next.prev=p.prev;p.prev.next=p.next;if(p.prevZ)p.prevZ.nextZ=p.nextZ;if(p.nextZ)p.nextZ.prevZ=p.prevZ}function Node(i,x,y){this.i=i;this.x=x;this.y=y;this.prev=null;this.next=null;this.z=null;this.prevZ=null;this.nextZ=null;this.steiner=false}function signedArea(data,start,end,dim){let sum=0;for(let i=start,j=end-dim;i2&&points[l-1].equals(points[0])){points.pop()}}function addContour(vertices,contour){for(let i=0;iNumber.EPSILON){const v_prev_len=Math.sqrt(v_prev_lensq);const v_next_len=Math.sqrt(v_next_x*v_next_x+v_next_y*v_next_y);const ptPrevShift_x=inPrev.x-v_prev_y/v_prev_len;const ptPrevShift_y=inPrev.y+v_prev_x/v_prev_len;const ptNextShift_x=inNext.x-v_next_y/v_next_len;const ptNextShift_y=inNext.y+v_next_x/v_next_len;const sf=((ptNextShift_x-ptPrevShift_x)*v_next_y-(ptNextShift_y-ptPrevShift_y)*v_next_x)/(v_prev_x*v_next_y-v_prev_y*v_next_x);v_trans_x=ptPrevShift_x+v_prev_x*sf-inPt.x;v_trans_y=ptPrevShift_y+v_prev_y*sf-inPt.y;const v_trans_lensq=v_trans_x*v_trans_x+v_trans_y*v_trans_y;if(v_trans_lensq<=2){return new Vector2(v_trans_x,v_trans_y)}else{shrink_by=Math.sqrt(v_trans_lensq/2)}}else{let direction_eq=false;if(v_prev_x>Number.EPSILON){if(v_next_x>Number.EPSILON){direction_eq=true}}else{if(v_prev_x<-Number.EPSILON){if(v_next_x<-Number.EPSILON){direction_eq=true}}else{if(Math.sign(v_prev_y)===Math.sign(v_next_y)){direction_eq=true}}}if(direction_eq){v_trans_x=-v_prev_y;v_trans_y=v_prev_x;shrink_by=Math.sqrt(v_prev_lensq)}else{v_trans_x=v_prev_x;v_trans_y=v_prev_y;shrink_by=Math.sqrt(v_prev_lensq/2)}}return new Vector2(v_trans_x/shrink_by,v_trans_y/shrink_by)}const contourMovements=[];for(let i=0,il=contour.length,j=il-1,k=i+1;i=0;b--){const t=b/bevelSegments;const z=bevelThickness*Math.cos(t*Math.PI/2);const bs=bevelSize*Math.sin(t*Math.PI/2)+bevelOffset;for(let i=0,il=contour.length;i=0){const j=i;let k=i-1;if(k<0)k=contour.length-1;for(let s=0,sl=steps+bevelSegments*2;s=endFrame)continue;times.push(track.times[j]);for(let k=0;kclip.tracks[i].times[0]){minStartTime=clip.tracks[i].times[0]}}for(let i=0;i=referenceTrack.times[lastIndex]){const startIndex=lastIndex*referenceValueSize+referenceOffset;const endIndex=startIndex+referenceValueSize-referenceOffset;referenceValue=AnimationUtils.arraySlice(referenceTrack.values,startIndex,endIndex)}else{const interpolant=referenceTrack.createInterpolant();const startIndex=referenceOffset;const endIndex=referenceValueSize-referenceOffset;interpolant.evaluate(referenceTime);referenceValue=AnimationUtils.arraySlice(interpolant.resultBuffer,startIndex,endIndex)}if(referenceTrackType==="quaternion"){const referenceQuat=(new Quaternion).fromArray(referenceValue).normalize().conjugate();referenceQuat.toArray(referenceValue)}const numTimes=targetTrack.times.length;for(let j=0;j=t0)){const t1global=pp[1];if(t=t0){break seek}}right=i1;i1=0;break linear_scan}break validate_interval}while(i1>>1;if(tendTime){--to}++to;if(from!==0||to!==nKeys){if(from>=to){to=Math.max(to,1);from=to-1}const stride=this.getValueSize();this.times=AnimationUtils.arraySlice(times,from,to);this.values=AnimationUtils.arraySlice(this.values,from*stride,to*stride)}return this}validate(){let valid=true;const valueSize=this.getValueSize();if(valueSize-Math.floor(valueSize)!==0){console.error("THREE.KeyframeTrack: Invalid value size in track.",this);valid=false}const times=this.times,values=this.values,nKeys=times.length;if(nKeys===0){console.error("THREE.KeyframeTrack: Track is empty.",this);valid=false}let prevTime=null;for(let i=0;i!==nKeys;i++){const currTime=times[i];if(typeof currTime==="number"&&isNaN(currTime)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,i,currTime);valid=false;break}if(prevTime!==null&&prevTime>currTime){console.error("THREE.KeyframeTrack: Out of order keys.",this,i,currTime,prevTime);valid=false;break}prevTime=currTime}if(values!==undefined){if(AnimationUtils.isTypedArray(values)){for(let i=0,n=values.length;i!==n;++i){const value=values[i];if(isNaN(value)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,i,value);valid=false;break}}}}return valid}optimize(){const times=AnimationUtils.arraySlice(this.times),values=AnimationUtils.arraySlice(this.values),stride=this.getValueSize(),smoothInterpolation=this.getInterpolation()===InterpolateSmooth,lastIndex=times.length-1;let writeIndex=1;for(let i=1;i0){times[writeIndex]=times[lastIndex];for(let readOffset=lastIndex*stride,writeOffset=writeIndex*stride,j=0;j!==stride;++j){values[writeOffset+j]=values[readOffset+j]}++writeIndex}if(writeIndex!==times.length){this.times=AnimationUtils.arraySlice(times,0,writeIndex);this.values=AnimationUtils.arraySlice(values,0,writeIndex*stride)}else{this.times=times;this.values=values}return this}clone(){const times=AnimationUtils.arraySlice(this.times,0);const values=AnimationUtils.arraySlice(this.values,0);const TypedKeyframeTrack=this.constructor;const track=new TypedKeyframeTrack(this.name,times,values);track.createInterpolant=this.createInterpolant;return track}}KeyframeTrack.prototype.TimeBufferType=Float32Array;KeyframeTrack.prototype.ValueBufferType=Float32Array;KeyframeTrack.prototype.DefaultInterpolation=InterpolateLinear;class BooleanKeyframeTrack extends KeyframeTrack{}BooleanKeyframeTrack.prototype.ValueTypeName="bool";BooleanKeyframeTrack.prototype.ValueBufferType=Array;BooleanKeyframeTrack.prototype.DefaultInterpolation=InterpolateDiscrete;BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear=undefined;BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class ColorKeyframeTrack extends KeyframeTrack{}ColorKeyframeTrack.prototype.ValueTypeName="color";class NumberKeyframeTrack extends KeyframeTrack{}NumberKeyframeTrack.prototype.ValueTypeName="number";class QuaternionLinearInterpolant extends Interpolant{constructor(parameterPositions,sampleValues,sampleSize,resultBuffer){super(parameterPositions,sampleValues,sampleSize,resultBuffer)}interpolate_(i1,t0,t,t1){const result=this.resultBuffer,values=this.sampleValues,stride=this.valueSize,alpha=(t-t0)/(t1-t0);let offset=i1*stride;for(let end=offset+stride;offset!==end;offset+=4){Quaternion.slerpFlat(result,0,values,offset-stride,values,offset,alpha)}return result}}class QuaternionKeyframeTrack extends KeyframeTrack{InterpolantFactoryMethodLinear(result){return new QuaternionLinearInterpolant(this.times,this.values,this.getValueSize(),result)}}QuaternionKeyframeTrack.prototype.ValueTypeName="quaternion";QuaternionKeyframeTrack.prototype.DefaultInterpolation=InterpolateLinear;QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class StringKeyframeTrack extends KeyframeTrack{}StringKeyframeTrack.prototype.ValueTypeName="string";StringKeyframeTrack.prototype.ValueBufferType=Array;StringKeyframeTrack.prototype.DefaultInterpolation=InterpolateDiscrete;StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear=undefined;StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class VectorKeyframeTrack extends KeyframeTrack{}VectorKeyframeTrack.prototype.ValueTypeName="vector";class AnimationClip{constructor(name,duration=-1,tracks,blendMode=NormalAnimationBlendMode){this.name=name;this.tracks=tracks;this.duration=duration;this.blendMode=blendMode;this.uuid=generateUUID();if(this.duration<0){this.resetDuration()}}static parse(json){const tracks=[],jsonTracks=json.tracks,frameTime=1/(json.fps||1);for(let i=0,n=jsonTracks.length;i!==n;++i){tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime))}const clip=new this(json.name,json.duration,tracks,json.blendMode);clip.uuid=json.uuid;return clip}static toJSON(clip){const tracks=[],clipTracks=clip.tracks;const json={name:clip.name,duration:clip.duration,tracks:tracks,uuid:clip.uuid,blendMode:clip.blendMode};for(let i=0,n=clipTracks.length;i!==n;++i){tracks.push(KeyframeTrack.toJSON(clipTracks[i]))}return json}static CreateFromMorphTargetSequence(name,morphTargetSequence,fps,noLoop){const numMorphTargets=morphTargetSequence.length;const tracks=[];for(let i=0;i1){const name=parts[1];let animationMorphTargets=animationToMorphTargets[name];if(!animationMorphTargets){animationToMorphTargets[name]=animationMorphTargets=[]}animationMorphTargets.push(morphTarget)}}const clips=[];for(const name in animationToMorphTargets){clips.push(this.CreateFromMorphTargetSequence(name,animationToMorphTargets[name],fps,noLoop))}return clips}static parseAnimation(animation,bones){if(!animation){console.error("THREE.AnimationClip: No animation in JSONLoader data.");return null}const addNonemptyTrack=function(trackType,trackName,animationKeys,propertyName,destTracks){if(animationKeys.length!==0){const times=[];const values=[];AnimationUtils.flattenJSON(animationKeys,times,values,propertyName);if(times.length!==0){destTracks.push(new trackType(trackName,times,values))}}};const tracks=[];const clipName=animation.name||"default";const fps=animation.fps||30;const blendMode=animation.blendMode;let duration=animation.length||-1;const hierarchyTracks=animation.hierarchy||[];for(let h=0;h0||url.search(/^data\:image\/jpeg/)===0;texture.format=isJPEG?RGBFormat:RGBAFormat;texture.needsUpdate=true;if(onLoad!==undefined){onLoad(texture)}}),onProgress,onError);return texture}}class Curve{constructor(){this.type="Curve";this.arcLengthDivisions=200}getPoint(){console.warn("THREE.Curve: .getPoint() not implemented.");return null}getPointAt(u,optionalTarget){const t=this.getUtoTmapping(u);return this.getPoint(t,optionalTarget)}getPoints(divisions=5){const points=[];for(let d=0;d<=divisions;d++){points.push(this.getPoint(d/divisions))}return points}getSpacedPoints(divisions=5){const points=[];for(let d=0;d<=divisions;d++){points.push(this.getPointAt(d/divisions))}return points}getLength(){const lengths=this.getLengths();return lengths[lengths.length-1]}getLengths(divisions=this.arcLengthDivisions){if(this.cacheArcLengths&&this.cacheArcLengths.length===divisions+1&&!this.needsUpdate){return this.cacheArcLengths}this.needsUpdate=false;const cache=[];let current,last=this.getPoint(0);let sum=0;cache.push(0);for(let p=1;p<=divisions;p++){current=this.getPoint(p/divisions);sum+=current.distanceTo(last);cache.push(sum);last=current}this.cacheArcLengths=cache;return cache}updateArcLengths(){this.needsUpdate=true;this.getLengths()}getUtoTmapping(u,distance){const arcLengths=this.getLengths();let i=0;const il=arcLengths.length;let targetArcLength;if(distance){targetArcLength=distance}else{targetArcLength=u*arcLengths[il-1]}let low=0,high=il-1,comparison;while(low<=high){i=Math.floor(low+(high-low)/2);comparison=arcLengths[i]-targetArcLength;if(comparison<0){low=i+1}else if(comparison>0){high=i-1}else{high=i;break}}i=high;if(arcLengths[i]===targetArcLength){return i/(il-1)}const lengthBefore=arcLengths[i];const lengthAfter=arcLengths[i+1];const segmentLength=lengthAfter-lengthBefore;const segmentFraction=(targetArcLength-lengthBefore)/segmentLength;const t=(i+segmentFraction)/(il-1);return t}getTangent(t,optionalTarget){const delta=1e-4;let t1=t-delta;let t2=t+delta;if(t1<0)t1=0;if(t2>1)t2=1;const pt1=this.getPoint(t1);const pt2=this.getPoint(t2);const tangent=optionalTarget||(pt1.isVector2?new Vector2:new Vector3);tangent.copy(pt2).sub(pt1).normalize();return tangent}getTangentAt(u,optionalTarget){const t=this.getUtoTmapping(u);return this.getTangent(t,optionalTarget)}computeFrenetFrames(segments,closed){const normal=new Vector3;const tangents=[];const normals=[];const binormals=[];const vec=new Vector3;const mat=new Matrix4;for(let i=0;i<=segments;i++){const u=i/segments;tangents[i]=this.getTangentAt(u,new Vector3);tangents[i].normalize()}normals[0]=new Vector3;binormals[0]=new Vector3;let min=Number.MAX_VALUE;const tx=Math.abs(tangents[0].x);const ty=Math.abs(tangents[0].y);const tz=Math.abs(tangents[0].z);if(tx<=min){min=tx;normal.set(1,0,0)}if(ty<=min){min=ty;normal.set(0,1,0)}if(tz<=min){normal.set(0,0,1)}vec.crossVectors(tangents[0],normal).normalize();normals[0].crossVectors(tangents[0],vec);binormals[0].crossVectors(tangents[0],normals[0]);for(let i=1;i<=segments;i++){normals[i]=normals[i-1].clone();binormals[i]=binormals[i-1].clone();vec.crossVectors(tangents[i-1],tangents[i]);if(vec.length()>Number.EPSILON){vec.normalize();const theta=Math.acos(clamp(tangents[i-1].dot(tangents[i]),-1,1));normals[i].applyMatrix4(mat.makeRotationAxis(vec,theta))}binormals[i].crossVectors(tangents[i],normals[i])}if(closed===true){let theta=Math.acos(clamp(normals[0].dot(normals[segments]),-1,1));theta/=segments;if(tangents[0].dot(vec.crossVectors(normals[0],normals[segments]))>0){theta=-theta}for(let i=1;i<=segments;i++){normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i],theta*i));binormals[i].crossVectors(tangents[i],normals[i])}}return{tangents:tangents,normals:normals,binormals:binormals}}clone(){return(new this.constructor).copy(this)}copy(source){this.arcLengthDivisions=source.arcLengthDivisions;return this}toJSON(){const data={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};data.arcLengthDivisions=this.arcLengthDivisions;data.type=this.type;return data}fromJSON(json){this.arcLengthDivisions=json.arcLengthDivisions;return this}}class EllipseCurve extends Curve{constructor(aX=0,aY=0,xRadius=1,yRadius=1,aStartAngle=0,aEndAngle=Math.PI*2,aClockwise=false,aRotation=0){super();this.type="EllipseCurve";this.aX=aX;this.aY=aY;this.xRadius=xRadius;this.yRadius=yRadius;this.aStartAngle=aStartAngle;this.aEndAngle=aEndAngle;this.aClockwise=aClockwise;this.aRotation=aRotation}getPoint(t,optionalTarget){const point=optionalTarget||new Vector2;const twoPi=Math.PI*2;let deltaAngle=this.aEndAngle-this.aStartAngle;const samePoints=Math.abs(deltaAngle)twoPi)deltaAngle-=twoPi;if(deltaAngle0?0:(Math.floor(Math.abs(intPoint)/l)+1)*l}else if(weight===0&&intPoint===l-1){intPoint=l-2;weight=1}let p0,p3;if(this.closed||intPoint>0){p0=points[(intPoint-1)%l]}else{tmp.subVectors(points[0],points[1]).add(points[0]);p0=tmp}const p1=points[intPoint%l];const p2=points[(intPoint+1)%l];if(this.closed||intPoint+2points.length-2?points.length-1:intPoint+1];const p3=points[intPoint>points.length-3?points.length-1:intPoint+2];point.set(CatmullRom(weight,p0.x,p1.x,p2.x,p3.x),CatmullRom(weight,p0.y,p1.y,p2.y,p3.y));return point}copy(source){super.copy(source);this.points=[];for(let i=0,l=source.points.length;i=d){const diff=curveLengths[i]-d;const curve=this.curves[i];const segmentLength=curve.getLength();const u=segmentLength===0?0:1-diff/segmentLength;return curve.getPointAt(u)}i++}return null}getLength(){const lens=this.getCurveLengths();return lens[lens.length-1]}updateArcLengths(){this.needsUpdate=true;this.cacheLengths=null;this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length){return this.cacheLengths}const lengths=[];let sums=0;for(let i=0,l=this.curves.length;i1&&!points[points.length-1].equals(points[0])){points.push(points[0])}return points}copy(source){super.copy(source);this.curves=[];for(let i=0,l=source.curves.length;i0){const firstPoint=curve.getPoint(0);if(!firstPoint.equals(this.currentPoint)){this.lineTo(firstPoint.x,firstPoint.y)}}this.curves.push(curve);const lastPoint=curve.getPoint(1);this.currentPoint.copy(lastPoint);return this}copy(source){super.copy(source);this.currentPoint.copy(source.currentPoint);return this}toJSON(){const data=super.toJSON();data.currentPoint=this.currentPoint.toArray();return data}fromJSON(json){super.fromJSON(json);this.currentPoint.fromArray(json.currentPoint);return this}}class Shape extends Path{constructor(points){super(points);this.uuid=generateUUID();this.type="Shape";this.holes=[]}getPointsHoles(divisions){const holesPts=[];for(let i=0,l=this.holes.length;i0){this.source.connect(this.filters[0]);for(let i=1,l=this.filters.length;i0){this.source.disconnect(this.filters[0]);for(let i=1,l=this.filters.length;i0){this._mixBufferRegionAdditive(buffer,offset,this._addIndex*stride,1,stride)}for(let i=stride,e=stride+stride;i!==e;++i){if(buffer[i]!==buffer[i+stride]){binding.setValue(buffer,offset);break}}}saveOriginalState(){const binding=this.binding;const buffer=this.buffer,stride=this.valueSize,originalValueOffset=stride*this._origIndex;binding.getValue(buffer,originalValueOffset);for(let i=stride,e=originalValueOffset;i!==e;++i){buffer[i]=buffer[originalValueOffset+i%stride]}this._setIdentity();this.cumulativeWeight=0;this.cumulativeWeightAdditive=0}restoreOriginalState(){const originalValueOffset=this.valueSize*3;this.binding.setValue(this.buffer,originalValueOffset)}_setAdditiveIdentityNumeric(){const startIndex=this._addIndex*this.valueSize;const endIndex=startIndex+this.valueSize;for(let i=startIndex;i=.5){for(let i=0;i!==stride;++i){buffer[dstOffset+i]=buffer[srcOffset+i]}}}_slerp(buffer,dstOffset,srcOffset,t){Quaternion.slerpFlat(buffer,dstOffset,buffer,dstOffset,buffer,srcOffset,t)}_slerpAdditive(buffer,dstOffset,srcOffset,t,stride){const workOffset=this._workIndex*stride;Quaternion.multiplyQuaternionsFlat(buffer,workOffset,buffer,dstOffset,buffer,srcOffset);Quaternion.slerpFlat(buffer,dstOffset,buffer,dstOffset,buffer,workOffset,t)}_lerp(buffer,dstOffset,srcOffset,t,stride){const s=1-t;for(let i=0;i!==stride;++i){const j=dstOffset+i;buffer[j]=buffer[j]*s+buffer[srcOffset+i]*t}}_lerpAdditive(buffer,dstOffset,srcOffset,t,stride){for(let i=0;i!==stride;++i){const j=dstOffset+i;buffer[j]=buffer[j]+buffer[srcOffset+i]*t}}}const _RESERVED_CHARS_RE="\\[\\]\\.:\\/";const _reservedRe=new RegExp("["+_RESERVED_CHARS_RE+"]","g");const _wordChar="[^"+_RESERVED_CHARS_RE+"]";const _wordCharOrDot="[^"+_RESERVED_CHARS_RE.replace("\\.","")+"]";const _directoryRe=/((?:WC+[\/:])*)/.source.replace("WC",_wordChar);const _nodeRe=/(WCOD+)?/.source.replace("WCOD",_wordCharOrDot);const _objectRe=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",_wordChar);const _propertyRe=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",_wordChar);const _trackRe=new RegExp(""+"^"+_directoryRe+_nodeRe+_objectRe+_propertyRe+"$");const _supportedObjectNames=["material","materials","bones"];class Composite{constructor(targetGroup,path,optionalParsedPath){const parsedPath=optionalParsedPath||PropertyBinding.parseTrackName(path);this._targetGroup=targetGroup;this._bindings=targetGroup.subscribe_(path,parsedPath)}getValue(array,offset){this.bind();const firstValidIndex=this._targetGroup.nCachedObjects_,binding=this._bindings[firstValidIndex];if(binding!==undefined)binding.getValue(array,offset)}setValue(array,offset){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].setValue(array,offset)}}bind(){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].bind()}}unbind(){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].unbind()}}}class PropertyBinding{constructor(rootNode,path,parsedPath){this.path=path;this.parsedPath=parsedPath||PropertyBinding.parseTrackName(path);this.node=PropertyBinding.findNode(rootNode,this.parsedPath.nodeName)||rootNode;this.rootNode=rootNode;this.getValue=this._getValue_unbound;this.setValue=this._setValue_unbound}static create(root,path,parsedPath){if(!(root&&root.isAnimationObjectGroup)){return new PropertyBinding(root,path,parsedPath)}else{return new PropertyBinding.Composite(root,path,parsedPath)}}static sanitizeNodeName(name){return name.replace(/\s/g,"_").replace(_reservedRe,"")}static parseTrackName(trackName){const matches=_trackRe.exec(trackName);if(!matches){throw new Error("PropertyBinding: Cannot parse trackName: "+trackName)}const results={nodeName:matches[2],objectName:matches[3],objectIndex:matches[4],propertyName:matches[5],propertyIndex:matches[6]};const lastDot=results.nodeName&&results.nodeName.lastIndexOf(".");if(lastDot!==undefined&&lastDot!==-1){const objectName=results.nodeName.substring(lastDot+1);if(_supportedObjectNames.indexOf(objectName)!==-1){results.nodeName=results.nodeName.substring(0,lastDot);results.objectName=objectName}}if(results.propertyName===null||results.propertyName.length===0){throw new Error("PropertyBinding: can not parse propertyName from trackName: "+trackName)}return results}static findNode(root,nodeName){if(!nodeName||nodeName===""||nodeName==="."||nodeName===-1||nodeName===root.name||nodeName===root.uuid){return root}if(root.skeleton){const bone=root.skeleton.getBoneByName(nodeName);if(bone!==undefined){return bone}}if(root.children){const searchNodeSubtree=function(children){for(let i=0;i0){const interpolants=this._interpolants;const propertyMixers=this._propertyBindings;switch(this.blendMode){case AdditiveAnimationBlendMode:for(let j=0,m=interpolants.length;j!==m;++j){interpolants[j].evaluate(clipTime);propertyMixers[j].accumulateAdditive(weight)}break;case NormalAnimationBlendMode:default:for(let j=0,m=interpolants.length;j!==m;++j){interpolants[j].evaluate(clipTime);propertyMixers[j].accumulate(accuIndex,weight)}}}}_updateWeight(time){let weight=0;if(this.enabled){weight=this.weight;const interpolant=this._weightInterpolant;if(interpolant!==null){const interpolantValue=interpolant.evaluate(time)[0];weight*=interpolantValue;if(time>interpolant.parameterPositions[1]){this.stopFading();if(interpolantValue===0){this.enabled=false}}}}this._effectiveWeight=weight;return weight}_updateTimeScale(time){let timeScale=0;if(!this.paused){timeScale=this.timeScale;const interpolant=this._timeScaleInterpolant;if(interpolant!==null){const interpolantValue=interpolant.evaluate(time)[0];timeScale*=interpolantValue;if(time>interpolant.parameterPositions[1]){this.stopWarping();if(timeScale===0){this.paused=true}else{this.timeScale=timeScale}}}}this._effectiveTimeScale=timeScale;return timeScale}_updateTime(deltaTime){const duration=this._clip.duration;const loop=this.loop;let time=this.time+deltaTime;let loopCount=this._loopCount;const pingPong=loop===LoopPingPong;if(deltaTime===0){if(loopCount===-1)return time;return pingPong&&(loopCount&1)===1?duration-time:time}if(loop===LoopOnce){if(loopCount===-1){this._loopCount=0;this._setEndings(true,true,false)}handle_stop:{if(time>=duration){time=duration}else if(time<0){time=0}else{this.time=time;break handle_stop}if(this.clampWhenFinished)this.paused=true;else this.enabled=false;this.time=time;this._mixer.dispatchEvent({type:"finished",action:this,direction:deltaTime<0?-1:1})}}else{if(loopCount===-1){if(deltaTime>=0){loopCount=0;this._setEndings(true,this.repetitions===0,pingPong)}else{this._setEndings(this.repetitions===0,true,pingPong)}}if(time>=duration||time<0){const loopDelta=Math.floor(time/duration);time-=duration*loopDelta;loopCount+=Math.abs(loopDelta);const pending=this.repetitions-loopCount;if(pending<=0){if(this.clampWhenFinished)this.paused=true;else this.enabled=false;time=deltaTime>0?duration:0;this.time=time;this._mixer.dispatchEvent({type:"finished",action:this,direction:deltaTime>0?1:-1})}else{if(pending===1){const atStart=deltaTime<0;this._setEndings(atStart,!atStart,pingPong)}else{this._setEndings(false,false,pingPong)}this._loopCount=loopCount;this.time=time;this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:loopDelta})}}else{this.time=time}if(pingPong&&(loopCount&1)===1){return duration-time}}return time}_setEndings(atStart,atEnd,pingPong){const settings=this._interpolantSettings;if(pingPong){settings.endingStart=ZeroSlopeEnding;settings.endingEnd=ZeroSlopeEnding}else{if(atStart){settings.endingStart=this.zeroSlopeAtStart?ZeroSlopeEnding:ZeroCurvatureEnding}else{settings.endingStart=WrapAroundEnding}if(atEnd){settings.endingEnd=this.zeroSlopeAtEnd?ZeroSlopeEnding:ZeroCurvatureEnding}else{settings.endingEnd=WrapAroundEnding}}}_scheduleFading(duration,weightNow,weightThen){const mixer=this._mixer,now=mixer.time;let interpolant=this._weightInterpolant;if(interpolant===null){interpolant=mixer._lendControlInterpolant();this._weightInterpolant=interpolant}const times=interpolant.parameterPositions,values=interpolant.sampleValues;times[0]=now;values[0]=weightNow;times[1]=now+duration;values[1]=weightThen;return this}}class AnimationMixer extends EventDispatcher$1{constructor(root){super();this._root=root;this._initMemoryManager();this._accuIndex=0;this.time=0;this.timeScale=1}_bindAction(action,prototypeAction){const root=action._localRoot||this._root,tracks=action._clip.tracks,nTracks=tracks.length,bindings=action._propertyBindings,interpolants=action._interpolants,rootUuid=root.uuid,bindingsByRoot=this._bindingsByRootAndName;let bindingsByName=bindingsByRoot[rootUuid];if(bindingsByName===undefined){bindingsByName={};bindingsByRoot[rootUuid]=bindingsByName}for(let i=0;i!==nTracks;++i){const track=tracks[i],trackName=track.name;let binding=bindingsByName[trackName];if(binding!==undefined){bindings[i]=binding}else{binding=bindings[i];if(binding!==undefined){if(binding._cacheIndex===null){++binding.referenceCount;this._addInactiveBinding(binding,rootUuid,trackName)}continue}const path=prototypeAction&&prototypeAction._propertyBindings[i].binding.parsedPath;binding=new PropertyMixer(PropertyBinding.create(root,trackName,path),track.ValueTypeName,track.getValueSize());++binding.referenceCount;this._addInactiveBinding(binding,rootUuid,trackName);bindings[i]=binding}interpolants[i].resultBuffer=binding.buffer}}_activateAction(action){if(!this._isActiveAction(action)){if(action._cacheIndex===null){const rootUuid=(action._localRoot||this._root).uuid,clipUuid=action._clip.uuid,actionsForClip=this._actionsByClip[clipUuid];this._bindAction(action,actionsForClip&&actionsForClip.knownActions[0]);this._addInactiveAction(action,clipUuid,rootUuid)}const bindings=action._propertyBindings;for(let i=0,n=bindings.length;i!==n;++i){const binding=bindings[i];if(binding.useCount++===0){this._lendBinding(binding);binding.saveOriginalState()}}this._lendAction(action)}}_deactivateAction(action){if(this._isActiveAction(action)){const bindings=action._propertyBindings;for(let i=0,n=bindings.length;i!==n;++i){const binding=bindings[i];if(--binding.useCount===0){binding.restoreOriginalState();this._takeBackBinding(binding)}}this._takeBackAction(action)}}_initMemoryManager(){this._actions=[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;const scope=this;this.stats={actions:{get total(){return scope._actions.length},get inUse(){return scope._nActiveActions}},bindings:{get total(){return scope._bindings.length},get inUse(){return scope._nActiveBindings}},controlInterpolants:{get total(){return scope._controlInterpolants.length},get inUse(){return scope._nActiveControlInterpolants}}}}_isActiveAction(action){const index=action._cacheIndex;return index!==null&&index=0;--i){actions[i].stop()}return this}update(deltaTime){deltaTime*=this.timeScale;const actions=this._actions,nActions=this._nActiveActions,time=this.time+=deltaTime,timeDirection=Math.sign(deltaTime),accuIndex=this._accuIndex^=1;for(let i=0;i!==nActions;++i){const action=actions[i];action._update(time,deltaTime,timeDirection,accuIndex)}const bindings=this._bindings,nBindings=this._nActiveBindings;for(let i=0;i!==nBindings;++i){bindings[i].apply(accuIndex)}return this}setTime(timeInSeconds){this.time=0;for(let i=0;i{const taskConfig={attributeIDs:this.defaultAttributeIDs,attributeTypes:this.defaultAttributeTypes,useUniqueIDs:false};this.decodeGeometry(buffer,taskConfig).then(onLoad).catch(onError)}),onProgress,onError)}decodeDracoFile(buffer,callback,attributeIDs,attributeTypes){const taskConfig={attributeIDs:attributeIDs||this.defaultAttributeIDs,attributeTypes:attributeTypes||this.defaultAttributeTypes,useUniqueIDs:!!attributeIDs};this.decodeGeometry(buffer,taskConfig).then(callback)}decodeGeometry(buffer,taskConfig){for(const attribute in taskConfig.attributeTypes){const type=taskConfig.attributeTypes[attribute];if(type.BYTES_PER_ELEMENT!==undefined){taskConfig.attributeTypes[attribute]=type.name}}const taskKey=JSON.stringify(taskConfig);if(_taskCache.has(buffer)){const cachedTask=_taskCache.get(buffer);if(cachedTask.key===taskKey){return cachedTask.promise}else if(buffer.byteLength===0){throw new Error("THREE.DRACOLoader: Unable to re-decode a buffer with different "+"settings. Buffer has already been transferred.")}}let worker;const taskID=this.workerNextTaskID++;const taskCost=buffer.byteLength;const geometryPending=this._getWorker(taskID,taskCost).then((_worker=>{worker=_worker;return new Promise(((resolve,reject)=>{worker._callbacks[taskID]={resolve:resolve,reject:reject};worker.postMessage({type:"decode",id:taskID,taskConfig:taskConfig,buffer:buffer},[buffer])}))})).then((message=>this._createGeometry(message.geometry)));geometryPending.catch((()=>true)).then((()=>{if(worker&&taskID){this._releaseTask(worker,taskID)}}));_taskCache.set(buffer,{key:taskKey,promise:geometryPending});return geometryPending}_createGeometry(geometryData){const geometry=new BufferGeometry;if(geometryData.index){geometry.setIndex(new BufferAttribute(geometryData.index.array,1))}for(let i=0;i{loader.load(url,resolve,undefined,reject)}))}preload(){this._initDecoder();return this}_initDecoder(){if(this.decoderPending)return this.decoderPending;const useJS=typeof WebAssembly!=="object"||this.decoderConfig.type==="js";const librariesPending=[];if(useJS){librariesPending.push(this._loadLibrary("draco_decoder.js","text"))}else{librariesPending.push(this._loadLibrary("draco_wasm_wrapper.js","text"));librariesPending.push(this._loadLibrary("draco_decoder.wasm","arraybuffer"))}this.decoderPending=Promise.all(librariesPending).then((libraries=>{const jsContent=libraries[0];if(!useJS){this.decoderConfig.wasmBinary=libraries[1]}const fn=DRACOWorker.toString();const body=["/* draco decoder */",jsContent,"","/* worker */",fn.substring(fn.indexOf("{")+1,fn.lastIndexOf("}"))].join("\n");this.workerSourceURL=URL.createObjectURL(new Blob([body]))}));return this.decoderPending}_getWorker(taskID,taskCost){return this._initDecoder().then((()=>{if(this.workerPool.lengthb._taskLoad?-1:1}))}const worker=this.workerPool[this.workerPool.length-1];worker._taskCosts[taskID]=taskCost;worker._taskLoad+=taskCost;return worker}))}_releaseTask(worker,taskID){worker._taskLoad-=worker._taskCosts[taskID];delete worker._callbacks[taskID];delete worker._taskCosts[taskID]}debug(){console.log("Task load: ",this.workerPool.map((worker=>worker._taskLoad)))}dispose(){for(let i=0;i{const draco=module.draco;const decoder=new draco.Decoder;const decoderBuffer=new draco.DecoderBuffer;decoderBuffer.Init(new Int8Array(buffer),buffer.byteLength);try{const geometry=decodeGeometry(draco,decoder,decoderBuffer,taskConfig);const buffers=geometry.attributes.map((attr=>attr.array.buffer));if(geometry.index)buffers.push(geometry.index.array.buffer);self.postMessage({type:"decode",id:message.id,geometry:geometry},buffers)}catch(error){console.error(error);self.postMessage({type:"error",id:message.id,error:error.message})}finally{draco.destroy(decoderBuffer);draco.destroy(decoder)}}));break}};function decodeGeometry(draco,decoder,decoderBuffer,taskConfig){const attributeIDs=taskConfig.attributeIDs;const attributeTypes=taskConfig.attributeTypes;let dracoGeometry;let decodingStatus;const geometryType=decoder.GetEncodedGeometryType(decoderBuffer);if(geometryType===draco.TRIANGULAR_MESH){dracoGeometry=new draco.Mesh;decodingStatus=decoder.DecodeBufferToMesh(decoderBuffer,dracoGeometry)}else if(geometryType===draco.POINT_CLOUD){dracoGeometry=new draco.PointCloud;decodingStatus=decoder.DecodeBufferToPointCloud(decoderBuffer,dracoGeometry)}else{throw new Error("THREE.DRACOLoader: Unexpected geometry type.")}if(!decodingStatus.ok()||dracoGeometry.ptr===0){throw new Error("THREE.DRACOLoader: Decoding failed: "+decodingStatus.error_msg())}const geometry={index:null,attributes:[]};for(const attributeName in attributeIDs){const attributeType=self[attributeTypes[attributeName]];let attribute;let attributeID;if(taskConfig.useUniqueIDs){attributeID=attributeIDs[attributeName];attribute=decoder.GetAttributeByUniqueId(dracoGeometry,attributeID)}else{attributeID=decoder.GetAttributeId(dracoGeometry,draco[attributeIDs[attributeName]]);if(attributeID===-1)continue;attribute=decoder.GetAttribute(dracoGeometry,attributeID)}geometry.attributes.push(decodeAttribute(draco,decoder,dracoGeometry,attributeName,attributeType,attribute))}if(geometryType===draco.TRIANGULAR_MESH){geometry.index=decodeIndex(draco,decoder,dracoGeometry)}draco.destroy(dracoGeometry);return geometry}function decodeIndex(draco,decoder,dracoGeometry){const numFaces=dracoGeometry.num_faces();const numIndices=numFaces*3;const byteLength=numIndices*4;const ptr=draco._malloc(byteLength);decoder.GetTrianglesUInt32Array(dracoGeometry,byteLength,ptr);const index=new Uint32Array(draco.HEAPF32.buffer,ptr,numIndices).slice();draco._free(ptr);return{array:index,itemSize:1}}function decodeAttribute(draco,decoder,dracoGeometry,attributeName,attributeType,attribute){const numComponents=attribute.num_components();const numPoints=dracoGeometry.num_points();const numValues=numPoints*numComponents;const byteLength=numValues*attributeType.BYTES_PER_ELEMENT;const dataType=getDracoDataType(draco,attributeType);const ptr=draco._malloc(byteLength);decoder.GetAttributeDataArrayForAllPoints(dracoGeometry,attribute,dataType,byteLength,ptr);const array=new attributeType(draco.HEAPF32.buffer,ptr,numValues).slice();draco._free(ptr);return{name:attributeName,array:array,itemSize:numComponents}}function getDracoDataType(draco,attributeType){switch(attributeType){case Float32Array:return draco.DT_FLOAT32;case Int8Array:return draco.DT_INT8;case Int16Array:return draco.DT_INT16;case Int32Array:return draco.DT_INT32;case Uint8Array:return draco.DT_UINT8;case Uint16Array:return draco.DT_UINT16;case Uint32Array:return draco.DT_UINT32}}}class GLTFLoader extends Loader{constructor(manager){super(manager);this.dracoLoader=null;this.ktx2Loader=null;this.meshoptDecoder=null;this.pluginCallbacks=[];this.register((function(parser){return new GLTFMaterialsClearcoatExtension(parser)}));this.register((function(parser){return new GLTFTextureBasisUExtension(parser)}));this.register((function(parser){return new GLTFTextureWebPExtension(parser)}));this.register((function(parser){return new GLTFMaterialsTransmissionExtension(parser)}));this.register((function(parser){return new GLTFLightsExtension(parser)}));this.register((function(parser){return new GLTFMeshoptCompression(parser)}))}load(url,onLoad,onProgress,onError){const scope=this;let resourcePath;if(this.resourcePath!==""){resourcePath=this.resourcePath}else if(this.path!==""){resourcePath=this.path}else{resourcePath=LoaderUtils.extractUrlBase(url)}this.manager.itemStart(url);const _onError=function(e){if(onError){onError(e)}else{console.error(e)}scope.manager.itemError(url);scope.manager.itemEnd(url)};const loader=new FileLoader(this.manager);loader.setPath(this.path);loader.setResponseType("arraybuffer");loader.setRequestHeader(this.requestHeader);loader.setWithCredentials(this.withCredentials);loader.load(url,(function(data){try{scope.parse(data,resourcePath,(function(gltf){onLoad(gltf);scope.manager.itemEnd(url)}),_onError)}catch(e){_onError(e)}}),onProgress,_onError)}setDRACOLoader(dracoLoader){this.dracoLoader=dracoLoader;return this}setDDSLoader(){throw new Error('THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".')}setKTX2Loader(ktx2Loader){this.ktx2Loader=ktx2Loader;return this}setMeshoptDecoder(meshoptDecoder){this.meshoptDecoder=meshoptDecoder;return this}register(callback){if(this.pluginCallbacks.indexOf(callback)===-1){this.pluginCallbacks.push(callback)}return this}unregister(callback){if(this.pluginCallbacks.indexOf(callback)!==-1){this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback),1)}return this}parse(data,path,onLoad,onError){let content;const extensions={};const plugins={};if(typeof data==="string"){content=data}else{const magic=LoaderUtils.decodeText(new Uint8Array(data,0,4));if(magic===BINARY_EXTENSION_HEADER_MAGIC){try{extensions[EXTENSIONS$1.KHR_BINARY_GLTF]=new GLTFBinaryExtension$1(data)}catch(error){if(onError)onError(error);return}content=extensions[EXTENSIONS$1.KHR_BINARY_GLTF].content}else{content=LoaderUtils.decodeText(new Uint8Array(data))}}const json=JSON.parse(content);if(json.asset===undefined||json.asset.version[0]<2){if(onError)onError(new Error("THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported."));return}const parser=new GLTFParser$1(json,{path:path||this.resourcePath||"",crossOrigin:this.crossOrigin,requestHeader:this.requestHeader,manager:this.manager,ktx2Loader:this.ktx2Loader,meshoptDecoder:this.meshoptDecoder});parser.fileLoader.setRequestHeader(this.requestHeader);for(let i=0;i=0&&plugins[extensionName]===undefined){console.warn('THREE.GLTFLoader: Unknown extension "'+extensionName+'".')}}}}parser.setExtensions(extensions);parser.setPlugins(plugins);parser.parse(onLoad,onError)}}function GLTFRegistry$1(){let objects={};return{get:function(key){return objects[key]},add:function(key,object){objects[key]=object},remove:function(key){delete objects[key]},removeAll:function(){objects={}}}}const EXTENSIONS$1={KHR_BINARY_GLTF:"KHR_binary_glTF",KHR_DRACO_MESH_COMPRESSION:"KHR_draco_mesh_compression",KHR_LIGHTS_PUNCTUAL:"KHR_lights_punctual",KHR_MATERIALS_CLEARCOAT:"KHR_materials_clearcoat",KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:"KHR_materials_pbrSpecularGlossiness",KHR_MATERIALS_TRANSMISSION:"KHR_materials_transmission",KHR_MATERIALS_UNLIT:"KHR_materials_unlit",KHR_TEXTURE_BASISU:"KHR_texture_basisu",KHR_TEXTURE_TRANSFORM:"KHR_texture_transform",KHR_MESH_QUANTIZATION:"KHR_mesh_quantization",EXT_TEXTURE_WEBP:"EXT_texture_webp",EXT_MESHOPT_COMPRESSION:"EXT_meshopt_compression"};class GLTFLightsExtension{constructor(parser){this.parser=parser;this.name=EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL;this.cache={refs:{},uses:{}}}_markDefs(){const parser=this.parser;const nodeDefs=this.parser.json.nodes||[];for(let nodeIndex=0,nodeLength=nodeDefs.length;nodeIndex=0){throw new Error("THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures")}else{return null}}return parser.loadTextureImage(textureIndex,source,loader)}}class GLTFTextureWebPExtension{constructor(parser){this.parser=parser;this.name=EXTENSIONS$1.EXT_TEXTURE_WEBP;this.isSupported=null}loadTexture(textureIndex){const name=this.name;const parser=this.parser;const json=parser.json;const textureDef=json.textures[textureIndex];if(!textureDef.extensions||!textureDef.extensions[name]){return null}const extension=textureDef.extensions[name];const source=json.images[extension.source];let loader=parser.textureLoader;if(source.uri){const handler=parser.options.manager.getHandler(source.uri);if(handler!==null)loader=handler}return this.detectSupport().then((function(isSupported){if(isSupported)return parser.loadTextureImage(textureIndex,source,loader);if(json.extensionsRequired&&json.extensionsRequired.indexOf(name)>=0){throw new Error("THREE.GLTFLoader: WebP required by asset but unsupported.")}return parser.loadTexture(textureIndex)}))}detectSupport(){if(!this.isSupported){this.isSupported=new Promise((function(resolve){const image=new Image;image.src="data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA";image.onload=image.onerror=function(){resolve(image.height===1)}}))}return this.isSupported}}class GLTFMeshoptCompression{constructor(parser){this.name=EXTENSIONS$1.EXT_MESHOPT_COMPRESSION;this.parser=parser}loadBufferView(index){const json=this.parser.json;const bufferView=json.bufferViews[index];if(bufferView.extensions&&bufferView.extensions[this.name]){const extensionDef=bufferView.extensions[this.name];const buffer=this.parser.getDependency("buffer",extensionDef.buffer);const decoder=this.parser.options.meshoptDecoder;if(!decoder||!decoder.supported){if(json.extensionsRequired&&json.extensionsRequired.indexOf(this.name)>=0){throw new Error("THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files")}else{return null}}return Promise.all([buffer,decoder.ready]).then((function(res){const byteOffset=extensionDef.byteOffset||0;const byteLength=extensionDef.byteLength||0;const count=extensionDef.count;const stride=extensionDef.byteStride;const result=new ArrayBuffer(count*stride);const source=new Uint8Array(res[0],byteOffset,byteLength);decoder.decodeGltfBuffer(new Uint8Array(result),count,stride,source,extensionDef.mode,extensionDef.filter);return result}))}else{return null}}}const BINARY_EXTENSION_HEADER_MAGIC="glTF";const BINARY_EXTENSION_HEADER_LENGTH$1=12;const BINARY_EXTENSION_CHUNK_TYPES={JSON:1313821514,BIN:5130562};class GLTFBinaryExtension$1{constructor(data){this.name=EXTENSIONS$1.KHR_BINARY_GLTF;this.content=null;this.body=null;const headerView=new DataView(data,0,BINARY_EXTENSION_HEADER_LENGTH$1);this.header={magic:LoaderUtils.decodeText(new Uint8Array(data.slice(0,4))),version:headerView.getUint32(4,true),length:headerView.getUint32(8,true)};if(this.header.magic!==BINARY_EXTENSION_HEADER_MAGIC){throw new Error("THREE.GLTFLoader: Unsupported glTF-Binary header.")}else if(this.header.version<2){throw new Error("THREE.GLTFLoader: Legacy binary file detected.")}const chunkContentsLength=this.header.length-BINARY_EXTENSION_HEADER_LENGTH$1;const chunkView=new DataView(data,BINARY_EXTENSION_HEADER_LENGTH$1);let chunkIndex=0;while(chunkIndex",specularMapParsFragmentChunk).replace("#include ",glossinessMapParsFragmentChunk).replace("#include ",specularMapFragmentChunk).replace("#include ",glossinessMapFragmentChunk).replace("#include ",lightPhysicalFragmentChunk)};Object.defineProperties(this,{specular:{get:function(){return uniforms.specular.value},set:function(v){uniforms.specular.value=v}},specularMap:{get:function(){return uniforms.specularMap.value},set:function(v){uniforms.specularMap.value=v;if(v){this.defines.USE_SPECULARMAP=""}else{delete this.defines.USE_SPECULARMAP}}},glossiness:{get:function(){return uniforms.glossiness.value},set:function(v){uniforms.glossiness.value=v}},glossinessMap:{get:function(){return uniforms.glossinessMap.value},set:function(v){uniforms.glossinessMap.value=v;if(v){this.defines.USE_GLOSSINESSMAP="";this.defines.USE_UV=""}else{delete this.defines.USE_GLOSSINESSMAP;delete this.defines.USE_UV}}}});delete this.metalness;delete this.roughness;delete this.metalnessMap;delete this.roughnessMap;this.setValues(params)}copy(source){super.copy(source);this.specularMap=source.specularMap;this.specular.copy(source.specular);this.glossinessMap=source.glossinessMap;this.glossiness=source.glossiness;delete this.metalness;delete this.roughness;delete this.metalnessMap;delete this.roughnessMap;return this}}class GLTFMaterialsPbrSpecularGlossinessExtension{constructor(){this.name=EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS;this.specularGlossinessParams=["color","map","lightMap","lightMapIntensity","aoMap","aoMapIntensity","emissive","emissiveIntensity","emissiveMap","bumpMap","bumpScale","normalMap","normalMapType","displacementMap","displacementScale","displacementBias","specularMap","specular","glossinessMap","glossiness","alphaMap","envMap","envMapIntensity","refractionRatio"]}getMaterialType(){return GLTFMeshStandardSGMaterial}extendParams(materialParams,materialDef,parser){const pbrSpecularGlossiness=materialDef.extensions[this.name];materialParams.color=new Color(1,1,1);materialParams.opacity=1;const pending=[];if(Array.isArray(pbrSpecularGlossiness.diffuseFactor)){const array=pbrSpecularGlossiness.diffuseFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3]}if(pbrSpecularGlossiness.diffuseTexture!==undefined){pending.push(parser.assignTexture(materialParams,"map",pbrSpecularGlossiness.diffuseTexture))}materialParams.emissive=new Color(0,0,0);materialParams.glossiness=pbrSpecularGlossiness.glossinessFactor!==undefined?pbrSpecularGlossiness.glossinessFactor:1;materialParams.specular=new Color(1,1,1);if(Array.isArray(pbrSpecularGlossiness.specularFactor)){materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor)}if(pbrSpecularGlossiness.specularGlossinessTexture!==undefined){const specGlossMapDef=pbrSpecularGlossiness.specularGlossinessTexture;pending.push(parser.assignTexture(materialParams,"glossinessMap",specGlossMapDef));pending.push(parser.assignTexture(materialParams,"specularMap",specGlossMapDef))}return Promise.all(pending)}createMaterial(materialParams){const material=new GLTFMeshStandardSGMaterial(materialParams);material.fog=true;material.color=materialParams.color;material.map=materialParams.map===undefined?null:materialParams.map;material.lightMap=null;material.lightMapIntensity=1;material.aoMap=materialParams.aoMap===undefined?null:materialParams.aoMap;material.aoMapIntensity=1;material.emissive=materialParams.emissive;material.emissiveIntensity=1;material.emissiveMap=materialParams.emissiveMap===undefined?null:materialParams.emissiveMap;material.bumpMap=materialParams.bumpMap===undefined?null:materialParams.bumpMap;material.bumpScale=1;material.normalMap=materialParams.normalMap===undefined?null:materialParams.normalMap;material.normalMapType=TangentSpaceNormalMap;if(materialParams.normalScale)material.normalScale=materialParams.normalScale;material.displacementMap=null;material.displacementScale=1;material.displacementBias=0;material.specularMap=materialParams.specularMap===undefined?null:materialParams.specularMap;material.specular=materialParams.specular;material.glossinessMap=materialParams.glossinessMap===undefined?null:materialParams.glossinessMap;material.glossiness=materialParams.glossiness;material.alphaMap=null;material.envMap=materialParams.envMap===undefined?null:materialParams.envMap;material.envMapIntensity=1;material.refractionRatio=.98;return material}}class GLTFMeshQuantizationExtension{constructor(){this.name=EXTENSIONS$1.KHR_MESH_QUANTIZATION}}class GLTFCubicSplineInterpolant extends Interpolant{constructor(parameterPositions,sampleValues,sampleSize,resultBuffer){super(parameterPositions,sampleValues,sampleSize,resultBuffer)}copySampleValue_(index){const result=this.resultBuffer,values=this.sampleValues,valueSize=this.valueSize,offset=index*valueSize*3+valueSize;for(let i=0;i!==valueSize;i++){result[i]=values[offset+i]}return result}}GLTFCubicSplineInterpolant.prototype.beforeStart_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.afterEnd_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.interpolate_=function(i1,t0,t,t1){const result=this.resultBuffer;const values=this.sampleValues;const stride=this.valueSize;const stride2=stride*2;const stride3=stride*3;const td=t1-t0;const p=(t-t0)/td;const pp=p*p;const ppp=pp*p;const offset1=i1*stride3;const offset0=offset1-stride3;const s2=-2*ppp+3*pp;const s3=ppp-pp;const s0=1-s2;const s1=s3-pp+p;for(let i=0;i!==stride;i++){const p0=values[offset0+i+stride];const m0=values[offset0+i+stride2]*td;const p1=values[offset1+i+stride];const m1=values[offset1+i]*td;result[i]=s0*p0+s1*m0+s2*p1+s3*m1}return result};const WEBGL_CONSTANTS$1={FLOAT:5126,FLOAT_MAT3:35675,FLOAT_MAT4:35676,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,LINEAR:9729,REPEAT:10497,SAMPLER_2D:35678,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,UNSIGNED_BYTE:5121,UNSIGNED_SHORT:5123};const WEBGL_COMPONENT_TYPES$1={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array};const WEBGL_FILTERS$1={9728:NearestFilter,9729:LinearFilter,9984:NearestMipmapNearestFilter,9985:LinearMipmapNearestFilter,9986:NearestMipmapLinearFilter,9987:LinearMipmapLinearFilter};const WEBGL_WRAPPINGS$1={33071:ClampToEdgeWrapping,33648:MirroredRepeatWrapping,10497:RepeatWrapping};const WEBGL_TYPE_SIZES$1={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16};const ATTRIBUTES={POSITION:"position",NORMAL:"normal",TANGENT:"tangent",TEXCOORD_0:"uv",TEXCOORD_1:"uv2",COLOR_0:"color",WEIGHTS_0:"skinWeight",JOINTS_0:"skinIndex"};const PATH_PROPERTIES$1={scale:"scale",translation:"position",rotation:"quaternion",weights:"morphTargetInfluences"};const INTERPOLATION$1={CUBICSPLINE:undefined,LINEAR:InterpolateLinear,STEP:InterpolateDiscrete};const ALPHA_MODES={OPAQUE:"OPAQUE",MASK:"MASK",BLEND:"BLEND"};function resolveURL$1(url,path){if(typeof url!=="string"||url==="")return"";if(/^https?:\/\//i.test(path)&&/^\//.test(url)){path=path.replace(/(^https?:\/\/[^\/]+).*/i,"$1")}if(/^(https?:)?\/\//i.test(url))return url;if(/^data:.*,.*$/i.test(url))return url;if(/^blob:.*$/i.test(url))return url;return path+url}function createDefaultMaterial$1(cache){if(cache["DefaultMaterial"]===undefined){cache["DefaultMaterial"]=new MeshStandardMaterial({color:16777215,emissive:0,metalness:1,roughness:1,transparent:false,depthTest:true,side:FrontSide})}return cache["DefaultMaterial"]}function addUnknownExtensionsToUserData(knownExtensions,object,objectDef){for(const name in objectDef.extensions){if(knownExtensions[name]===undefined){object.userData.gltfExtensions=object.userData.gltfExtensions||{};object.userData.gltfExtensions[name]=objectDef.extensions[name]}}}function assignExtrasToUserData(object,gltfDef){if(gltfDef.extras!==undefined){if(typeof gltfDef.extras==="object"){Object.assign(object.userData,gltfDef.extras)}else{console.warn("THREE.GLTFLoader: Ignoring primitive type .extras, "+gltfDef.extras)}}}function addMorphTargets(geometry,targets,parser){let hasMorphPosition=false;let hasMorphNormal=false;for(let i=0,il=targets.length;i=2)bufferAttribute.setY(index,sparseValues[i*itemSize+1]);if(itemSize>=3)bufferAttribute.setZ(index,sparseValues[i*itemSize+2]);if(itemSize>=4)bufferAttribute.setW(index,sparseValues[i*itemSize+3]);if(itemSize>=5)throw new Error("THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.")}}return bufferAttribute}))}loadTexture(textureIndex){const json=this.json;const options=this.options;const textureDef=json.textures[textureIndex];const source=json.images[textureDef.source];let loader=this.textureLoader;if(source.uri){const handler=options.manager.getHandler(source.uri);if(handler!==null)loader=handler}return this.loadTextureImage(textureIndex,source,loader)}loadTextureImage(textureIndex,source,loader){const parser=this;const json=this.json;const options=this.options;const textureDef=json.textures[textureIndex];const URL=self.URL||self.webkitURL;let sourceURI=source.uri;let isObjectURL=false;let hasAlpha=true;if(source.mimeType==="image/jpeg")hasAlpha=false;if(source.bufferView!==undefined){sourceURI=parser.getDependency("bufferView",source.bufferView).then((function(bufferView){if(source.mimeType==="image/png"){const colorType=new DataView(bufferView,25,1).getUint8(0,false);hasAlpha=colorType===6||colorType===4||colorType===3}isObjectURL=true;const blob=new Blob([bufferView],{type:source.mimeType});sourceURI=URL.createObjectURL(blob);return sourceURI}))}else if(source.uri===undefined){throw new Error("THREE.GLTFLoader: Image "+textureIndex+" is missing URI and bufferView")}return Promise.resolve(sourceURI).then((function(sourceURI){return new Promise((function(resolve,reject){let onLoad=resolve;if(loader.isImageBitmapLoader===true){onLoad=function(imageBitmap){resolve(new CanvasTexture(imageBitmap))}}loader.load(resolveURL$1(sourceURI,options.path),onLoad,undefined,reject)}))})).then((function(texture){if(isObjectURL===true){URL.revokeObjectURL(sourceURI)}texture.flipY=false;if(textureDef.name)texture.name=textureDef.name;if(!hasAlpha)texture.format=RGBFormat;const samplers=json.samplers||{};const sampler=samplers[textureDef.sampler]||{};texture.magFilter=WEBGL_FILTERS$1[sampler.magFilter]||LinearFilter;texture.minFilter=WEBGL_FILTERS$1[sampler.minFilter]||LinearMipmapLinearFilter;texture.wrapS=WEBGL_WRAPPINGS$1[sampler.wrapS]||RepeatWrapping;texture.wrapT=WEBGL_WRAPPINGS$1[sampler.wrapT]||RepeatWrapping;parser.associations.set(texture,{type:"textures",index:textureIndex});return texture}))}assignTexture(materialParams,mapName,mapDef){const parser=this;return this.getDependency("texture",mapDef.index).then((function(texture){if(mapDef.texCoord!==undefined&&mapDef.texCoord!=0&&!(mapName==="aoMap"&&mapDef.texCoord==1)){console.warn("THREE.GLTFLoader: Custom UV set "+mapDef.texCoord+" for texture "+mapName+" not yet supported.")}if(parser.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM]){const transform=mapDef.extensions!==undefined?mapDef.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM]:undefined;if(transform){const gltfReference=parser.associations.get(texture);texture=parser.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM].extendTexture(texture,transform);parser.associations.set(texture,gltfReference)}}materialParams[mapName]=texture}))}assignFinalMaterial(mesh){const geometry=mesh.geometry;let material=mesh.material;const useVertexTangents=geometry.attributes.tangent!==undefined;const useVertexColors=geometry.attributes.color!==undefined;const useFlatShading=geometry.attributes.normal===undefined;const useSkinning=mesh.isSkinnedMesh===true;const useMorphTargets=Object.keys(geometry.morphAttributes).length>0;const useMorphNormals=useMorphTargets&&geometry.morphAttributes.normal!==undefined;if(mesh.isPoints){const cacheKey="PointsMaterial:"+material.uuid;let pointsMaterial=this.cache.get(cacheKey);if(!pointsMaterial){pointsMaterial=new PointsMaterial;Material.prototype.copy.call(pointsMaterial,material);pointsMaterial.color.copy(material.color);pointsMaterial.map=material.map;pointsMaterial.sizeAttenuation=false;this.cache.add(cacheKey,pointsMaterial)}material=pointsMaterial}else if(mesh.isLine){const cacheKey="LineBasicMaterial:"+material.uuid;let lineMaterial=this.cache.get(cacheKey);if(!lineMaterial){lineMaterial=new LineBasicMaterial;Material.prototype.copy.call(lineMaterial,material);lineMaterial.color.copy(material.color);this.cache.add(cacheKey,lineMaterial)}material=lineMaterial}if(useVertexTangents||useVertexColors||useFlatShading||useSkinning||useMorphTargets){let cacheKey="ClonedMaterial:"+material.uuid+":";if(material.isGLTFSpecularGlossinessMaterial)cacheKey+="specular-glossiness:";if(useSkinning)cacheKey+="skinning:";if(useVertexTangents)cacheKey+="vertex-tangents:";if(useVertexColors)cacheKey+="vertex-colors:";if(useFlatShading)cacheKey+="flat-shading:";if(useMorphTargets)cacheKey+="morph-targets:";if(useMorphNormals)cacheKey+="morph-normals:";let cachedMaterial=this.cache.get(cacheKey);if(!cachedMaterial){cachedMaterial=material.clone();if(useSkinning)cachedMaterial.skinning=true;if(useVertexColors)cachedMaterial.vertexColors=true;if(useFlatShading)cachedMaterial.flatShading=true;if(useMorphTargets)cachedMaterial.morphTargets=true;if(useMorphNormals)cachedMaterial.morphNormals=true;if(useVertexTangents){cachedMaterial.vertexTangents=true;if(cachedMaterial.normalScale)cachedMaterial.normalScale.y*=-1;if(cachedMaterial.clearcoatNormalScale)cachedMaterial.clearcoatNormalScale.y*=-1}this.cache.add(cacheKey,cachedMaterial);this.associations.set(cachedMaterial,this.associations.get(material))}material=cachedMaterial}if(material.aoMap&&geometry.attributes.uv2===undefined&&geometry.attributes.uv!==undefined){geometry.setAttribute("uv2",geometry.attributes.uv)}mesh.material=material}getMaterialType(){return MeshStandardMaterial}loadMaterial(materialIndex){const parser=this;const json=this.json;const extensions=this.extensions;const materialDef=json.materials[materialIndex];let materialType;const materialParams={};const materialExtensions=materialDef.extensions||{};const pending=[];if(materialExtensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]){const sgExtension=extensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];materialType=sgExtension.getMaterialType();pending.push(sgExtension.extendParams(materialParams,materialDef,parser))}else if(materialExtensions[EXTENSIONS$1.KHR_MATERIALS_UNLIT]){const kmuExtension=extensions[EXTENSIONS$1.KHR_MATERIALS_UNLIT];materialType=kmuExtension.getMaterialType();pending.push(kmuExtension.extendParams(materialParams,materialDef,parser))}else{const metallicRoughness=materialDef.pbrMetallicRoughness||{};materialParams.color=new Color(1,1,1);materialParams.opacity=1;if(Array.isArray(metallicRoughness.baseColorFactor)){const array=metallicRoughness.baseColorFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3]}if(metallicRoughness.baseColorTexture!==undefined){pending.push(parser.assignTexture(materialParams,"map",metallicRoughness.baseColorTexture))}materialParams.metalness=metallicRoughness.metallicFactor!==undefined?metallicRoughness.metallicFactor:1;materialParams.roughness=metallicRoughness.roughnessFactor!==undefined?metallicRoughness.roughnessFactor:1;if(metallicRoughness.metallicRoughnessTexture!==undefined){pending.push(parser.assignTexture(materialParams,"metalnessMap",metallicRoughness.metallicRoughnessTexture));pending.push(parser.assignTexture(materialParams,"roughnessMap",metallicRoughness.metallicRoughnessTexture))}materialType=this._invokeOne((function(ext){return ext.getMaterialType&&ext.getMaterialType(materialIndex)}));pending.push(Promise.all(this._invokeAll((function(ext){return ext.extendMaterialParams&&ext.extendMaterialParams(materialIndex,materialParams)}))))}if(materialDef.doubleSided===true){materialParams.side=DoubleSide}const alphaMode=materialDef.alphaMode||ALPHA_MODES.OPAQUE;if(alphaMode===ALPHA_MODES.BLEND){materialParams.transparent=true;materialParams.depthWrite=false}else{materialParams.transparent=false;if(alphaMode===ALPHA_MODES.MASK){materialParams.alphaTest=materialDef.alphaCutoff!==undefined?materialDef.alphaCutoff:.5}}if(materialDef.normalTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"normalMap",materialDef.normalTexture));materialParams.normalScale=new Vector2(1,-1);if(materialDef.normalTexture.scale!==undefined){materialParams.normalScale.set(materialDef.normalTexture.scale,-materialDef.normalTexture.scale)}}if(materialDef.occlusionTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"aoMap",materialDef.occlusionTexture));if(materialDef.occlusionTexture.strength!==undefined){materialParams.aoMapIntensity=materialDef.occlusionTexture.strength}}if(materialDef.emissiveFactor!==undefined&&materialType!==MeshBasicMaterial){materialParams.emissive=(new Color).fromArray(materialDef.emissiveFactor)}if(materialDef.emissiveTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"emissiveMap",materialDef.emissiveTexture))}return Promise.all(pending).then((function(){let material;if(materialType===GLTFMeshStandardSGMaterial){material=extensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams)}else{material=new materialType(materialParams)}if(materialDef.name)material.name=materialDef.name;if(material.map)material.map.encoding=sRGBEncoding;if(material.emissiveMap)material.emissiveMap.encoding=sRGBEncoding;assignExtrasToUserData(material,materialDef);parser.associations.set(material,{type:"materials",index:materialIndex});if(materialDef.extensions)addUnknownExtensionsToUserData(extensions,material,materialDef);return material}))}createUniqueName(originalName){const sanitizedName=PropertyBinding.sanitizeNodeName(originalName||"");let name=sanitizedName;for(let i=1;this.nodeNamesUsed[name];++i){name=sanitizedName+"_"+i}this.nodeNamesUsed[name]=true;return name}loadGeometries(primitives){const parser=this;const extensions=this.extensions;const cache=this.primitiveCache;function createDracoPrimitive(primitive){return extensions[EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(primitive,parser).then((function(geometry){return addPrimitiveAttributes(geometry,primitive,parser)}))}const pending=[];for(let i=0,il=primitives.length;i0){updateMorphTargets(mesh,meshDef)}mesh.name=parser.createUniqueName(meshDef.name||"mesh_"+meshIndex);assignExtrasToUserData(mesh,meshDef);if(primitive.extensions)addUnknownExtensionsToUserData(extensions,mesh,primitive);parser.assignFinalMaterial(mesh);meshes.push(mesh)}if(meshes.length===1){return meshes[0]}const group=new Group;for(let i=0,il=meshes.length;i1){node=new Group}else if(objects.length===1){node=objects[0]}else{node=new Object3D}if(node!==objects[0]){for(let i=0,il=objects.length;i>8&255]+_lut[d0>>16&255]+_lut[d0>>24&255]+"-"+_lut[d1&255]+_lut[d1>>8&255]+"-"+_lut[d1>>16&15|64]+_lut[d1>>24&255]+"-"+_lut[d2&63|128]+_lut[d2>>8&255]+"-"+_lut[d2>>16&255]+_lut[d2>>24&255]+_lut[d3&255]+_lut[d3>>8&255]+_lut[d3>>16&255]+_lut[d3>>24&255];return uuid.toLowerCase()}function clamp(value,min,max){return Math.max(min,Math.min(max,value))}function euclideanModulo(n,m){return(n%m+m)%m}function mapLinear(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1)}function inverseLerp(x,y,value){if(x!==y){return(value-x)/(y-x)}else{return 0}}function lerp(x,y,t){return(1-t)*x+t*y}function damp(x,y,lambda,dt){return lerp(x,y,1-Math.exp(-lambda*dt))}function pingpong(x,length=1){return length-Math.abs(euclideanModulo(x,length*2)-length)}function smoothstep(x,min,max){if(x<=min)return 0;if(x>=max)return 1;x=(x-min)/(max-min);return x*x*(3-2*x)}function smootherstep(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)}function randInt(low,high){return low+Math.floor(Math.random()*(high-low+1))}function randFloat(low,high){return low+Math.random()*(high-low)}function randFloatSpread(range){return range*(.5-Math.random())}function seededRandom(s){if(s!==undefined)_seed=s;let t=_seed+=1831565813;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296}function degToRad(degrees){return degrees*DEG2RAD}function radToDeg(radians){return radians*RAD2DEG}function isPowerOfTwo(value){return(value&value-1)===0&&value!==0}function ceilPowerOfTwo(value){return Math.pow(2,Math.ceil(Math.log(value)/Math.LN2))}function floorPowerOfTwo(value){return Math.pow(2,Math.floor(Math.log(value)/Math.LN2))}function setQuaternionFromProperEuler(q,a,b,c,order){const cos=Math.cos;const sin=Math.sin;const c2=cos(b/2);const s2=sin(b/2);const c13=cos((a+c)/2);const s13=sin((a+c)/2);const c1_3=cos((a-c)/2);const s1_3=sin((a-c)/2);const c3_1=cos((c-a)/2);const s3_1=sin((c-a)/2);switch(order){case"XYX":q.set(c2*s13,s2*c1_3,s2*s1_3,c2*c13);break;case"YZY":q.set(s2*s1_3,c2*s13,s2*c1_3,c2*c13);break;case"ZXZ":q.set(s2*c1_3,s2*s1_3,c2*s13,c2*c13);break;case"XZX":q.set(c2*s13,s2*s3_1,s2*c3_1,c2*c13);break;case"YXY":q.set(s2*c3_1,c2*s13,s2*s3_1,c2*c13);break;case"ZYZ":q.set(s2*s3_1,s2*c3_1,c2*s13,c2*c13);break;default:console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+order)}}function denormalize$1(value,array){switch(array.constructor){case Float32Array:return value;case Uint16Array:return value/65535;case Uint8Array:return value/255;case Int16Array:return Math.max(value/32767,-1);case Int8Array:return Math.max(value/127,-1);default:throw new Error("Invalid component type.")}}function normalize(value,array){switch(array.constructor){case Float32Array:return value;case Uint16Array:return Math.round(value*65535);case Uint8Array:return Math.round(value*255);case Int16Array:return Math.round(value*32767);case Int8Array:return Math.round(value*127);default:throw new Error("Invalid component type.")}}var MathUtils=Object.freeze({__proto__:null,DEG2RAD:DEG2RAD,RAD2DEG:RAD2DEG,generateUUID:generateUUID,clamp:clamp,euclideanModulo:euclideanModulo,mapLinear:mapLinear,inverseLerp:inverseLerp,lerp:lerp,damp:damp,pingpong:pingpong,smoothstep:smoothstep,smootherstep:smootherstep,randInt:randInt,randFloat:randFloat,randFloatSpread:randFloatSpread,seededRandom:seededRandom,degToRad:degToRad,radToDeg:radToDeg,isPowerOfTwo:isPowerOfTwo,ceilPowerOfTwo:ceilPowerOfTwo,floorPowerOfTwo:floorPowerOfTwo,setQuaternionFromProperEuler:setQuaternionFromProperEuler,normalize:normalize,denormalize:denormalize$1});class Vector2{constructor(x=0,y=0){this.x=x;this.y=y}get width(){return this.x}set width(value){this.x=value}get height(){return this.y}set height(value){this.y=value}set(x,y){this.x=x;this.y=y;return this}setScalar(scalar){this.x=scalar;this.y=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setComponent(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(index){switch(index){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+index)}}clone(){return new this.constructor(this.x,this.y)}copy(v){this.x=v.x;this.y=v.y;return this}add(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x+=s;this.y+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x-=s;this.y-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this}multiply(v){this.x*=v.x;this.y*=v.y;return this}multiplyScalar(scalar){this.x*=scalar;this.y*=scalar;return this}divide(v){this.x/=v.x;this.y/=v.y;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}applyMatrix3(m){const x=this.x,y=this.y;const 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(v){this.x=Math.min(this.x,v.x);this.y=Math.min(this.y,v.y);return this}max(v){this.x=Math.max(this.x,v.x);this.y=Math.max(this.y,v.y);return this}clamp(min,max){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(minVal,maxVal){this.x=Math.max(minVal,Math.min(maxVal,this.x));this.y=Math.max(minVal,Math.min(maxVal,this.y));return this}clampLength(min,max){const length=this.length();return this.divideScalar(length||1).multiplyScalar(Math.max(min,Math.min(max,length)))}floor(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this}ceil(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this}round(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}roundToZero(){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(){this.x=-this.x;this.y=-this.y;return this}dot(v){return this.x*v.x+this.y*v.y}cross(v){return this.x*v.y-this.y*v.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){const angle=Math.atan2(-this.y,-this.x)+Math.PI;return angle}distanceTo(v){return Math.sqrt(this.distanceToSquared(v))}distanceToSquared(v){const dx=this.x-v.x,dy=this.y-v.y;return dx*dx+dy*dy}manhattanDistanceTo(v){return Math.abs(this.x-v.x)+Math.abs(this.y-v.y)}setLength(length){return this.normalize().multiplyScalar(length)}lerp(v,alpha){this.x+=(v.x-this.x)*alpha;this.y+=(v.y-this.y)*alpha;return this}lerpVectors(v1,v2,alpha){this.x=v1.x+(v2.x-v1.x)*alpha;this.y=v1.y+(v2.y-v1.y)*alpha;return this}equals(v){return v.x===this.x&&v.y===this.y}fromArray(array,offset=0){this.x=array[offset];this.y=array[offset+1];return this}toArray(array=[],offset=0){array[offset]=this.x;array[offset+1]=this.y;return array}fromBufferAttribute(attribute,index,offset){if(offset!==undefined){console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute().")}this.x=attribute.getX(index);this.y=attribute.getY(index);return this}rotateAround(center,angle){const c=Math.cos(angle),s=Math.sin(angle);const x=this.x-center.x;const y=this.y-center.y;this.x=x*c-y*s+center.x;this.y=x*s+y*c+center.y;return this}random(){this.x=Math.random();this.y=Math.random();return this}*[Symbol.iterator](){yield this.x;yield this.y}}Vector2.prototype.isVector2=true;class Matrix3{constructor(){this.elements=[1,0,0,0,1,0,0,0,1];if(arguments.length>0){console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")}}set(n11,n12,n13,n21,n22,n23,n31,n32,n33){const 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(){this.set(1,0,0,0,1,0,0,0,1);return this}copy(m){const te=this.elements;const 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}extractBasis(xAxis,yAxis,zAxis){xAxis.setFromMatrix3Column(this,0);yAxis.setFromMatrix3Column(this,1);zAxis.setFromMatrix3Column(this,2);return this}setFromMatrix4(m){const 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}multiply(m){return this.multiplyMatrices(this,m)}premultiply(m){return this.multiplyMatrices(m,this)}multiplyMatrices(a,b){const ae=a.elements;const be=b.elements;const te=this.elements;const a11=ae[0],a12=ae[3],a13=ae[6];const a21=ae[1],a22=ae[4],a23=ae[7];const a31=ae[2],a32=ae[5],a33=ae[8];const b11=be[0],b12=be[3],b13=be[6];const b21=be[1],b22=be[4],b23=be[7];const 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(s){const 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(){const te=this.elements;const 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}invert(){const te=this.elements,n11=te[0],n21=te[1],n31=te[2],n12=te[3],n22=te[4],n32=te[5],n13=te[6],n23=te[7],n33=te[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)return this.set(0,0,0,0,0,0,0,0,0);const 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(){let tmp;const 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(matrix4){return this.setFromMatrix4(matrix4).invert().transpose()}transposeIntoArray(r){const 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(tx,ty,sx,sy,rotation,cx,cy){const c=Math.cos(rotation);const 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);return this}scale(sx,sy){const te=this.elements;te[0]*=sx;te[3]*=sx;te[6]*=sx;te[1]*=sy;te[4]*=sy;te[7]*=sy;return this}rotate(theta){const c=Math.cos(theta);const s=Math.sin(theta);const te=this.elements;const a11=te[0],a12=te[3],a13=te[6];const 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(tx,ty){const 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(matrix){const te=this.elements;const me=matrix.elements;for(let i=0;i<9;i++){if(te[i]!==me[i])return false}return true}fromArray(array,offset=0){for(let i=0;i<9;i++){this.elements[i]=array[i+offset]}return this}toArray(array=[],offset=0){const 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}clone(){return(new this.constructor).fromArray(this.elements)}}Matrix3.prototype.isMatrix3=true;function arrayNeedsUint32(array){for(let i=array.length-1;i>=0;--i){if(array[i]>65535)return true}return false}function createElementNS(name){return document.createElementNS("http://www.w3.org/1999/xhtml",name)}function SRGBToLinear(c){return c<.04045?c*.0773993808:Math.pow(c*.9478672986+.0521327014,2.4)}function LinearToSRGB(c){return c<.0031308?c*12.92:1.055*Math.pow(c,.41666)-.055}const FN={[SRGBColorSpace]:{[LinearSRGBColorSpace]:SRGBToLinear},[LinearSRGBColorSpace]:{[SRGBColorSpace]:LinearToSRGB}};const ColorManagement={legacyMode:true,get workingColorSpace(){return LinearSRGBColorSpace},set workingColorSpace(colorSpace){console.warn("THREE.ColorManagement: .workingColorSpace is readonly.")},convert:function(color,sourceColorSpace,targetColorSpace){if(this.legacyMode||sourceColorSpace===targetColorSpace||!sourceColorSpace||!targetColorSpace){return color}if(FN[sourceColorSpace]&&FN[sourceColorSpace][targetColorSpace]!==undefined){const fn=FN[sourceColorSpace][targetColorSpace];color.r=fn(color.r);color.g=fn(color.g);color.b=fn(color.b);return color}throw new Error("Unsupported color space conversion.")},fromWorkingColorSpace:function(color,targetColorSpace){return this.convert(color,this.workingColorSpace,targetColorSpace)},toWorkingColorSpace:function(color,sourceColorSpace){return this.convert(color,sourceColorSpace,this.workingColorSpace)}};const _colorKeywords={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};const _rgb={r:0,g:0,b:0};const _hslA={h:0,s:0,l:0};const _hslB={h:0,s:0,l:0};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}function toComponents(source,target){target.r=source.r;target.g=source.g;target.b=source.b;return target}class Color{constructor(r,g,b){if(g===undefined&&b===undefined){return this.set(r)}return this.setRGB(r,g,b)}set(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(scalar){this.r=scalar;this.g=scalar;this.b=scalar;return this}setHex(hex,colorSpace=SRGBColorSpace){hex=Math.floor(hex);this.r=(hex>>16&255)/255;this.g=(hex>>8&255)/255;this.b=(hex&255)/255;ColorManagement.toWorkingColorSpace(this,colorSpace);return this}setRGB(r,g,b,colorSpace=LinearSRGBColorSpace){this.r=r;this.g=g;this.b=b;ColorManagement.toWorkingColorSpace(this,colorSpace);return this}setHSL(h,s,l,colorSpace=LinearSRGBColorSpace){h=euclideanModulo(h,1);s=clamp(s,0,1);l=clamp(l,0,1);if(s===0){this.r=this.g=this.b=l}else{const p=l<=.5?l*(1+s):l+s-l*s;const 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)}ColorManagement.toWorkingColorSpace(this,colorSpace);return this}setStyle(style,colorSpace=SRGBColorSpace){function handleAlpha(string){if(string===undefined)return;if(parseFloat(string)<1){console.warn("THREE.Color: Alpha component of "+style+" will be ignored.")}}let m;if(m=/^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(style)){let color;const name=m[1];const components=m[2];switch(name){case"rgb":case"rgba":if(color=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){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;ColorManagement.toWorkingColorSpace(this,colorSpace);handleAlpha(color[4]);return this}if(color=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){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;ColorManagement.toWorkingColorSpace(this,colorSpace);handleAlpha(color[4]);return this}break;case"hsl":case"hsla":if(color=/^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)){const h=parseFloat(color[1])/360;const s=parseInt(color[2],10)/100;const l=parseInt(color[3],10)/100;handleAlpha(color[4]);return this.setHSL(h,s,l,colorSpace)}break}}else if(m=/^\#([A-Fa-f\d]+)$/.exec(style)){const hex=m[1];const size=hex.length;if(size===3){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;ColorManagement.toWorkingColorSpace(this,colorSpace);return this}else if(size===6){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;ColorManagement.toWorkingColorSpace(this,colorSpace);return this}}if(style&&style.length>0){return this.setColorName(style,colorSpace)}return this}setColorName(style,colorSpace=SRGBColorSpace){const hex=_colorKeywords[style.toLowerCase()];if(hex!==undefined){this.setHex(hex,colorSpace)}else{console.warn("THREE.Color: Unknown color "+style)}return this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(color){this.r=color.r;this.g=color.g;this.b=color.b;return this}copySRGBToLinear(color){this.r=SRGBToLinear(color.r);this.g=SRGBToLinear(color.g);this.b=SRGBToLinear(color.b);return this}copyLinearToSRGB(color){this.r=LinearToSRGB(color.r);this.g=LinearToSRGB(color.g);this.b=LinearToSRGB(color.b);return this}convertSRGBToLinear(){this.copySRGBToLinear(this);return this}convertLinearToSRGB(){this.copyLinearToSRGB(this);return this}getHex(colorSpace=SRGBColorSpace){ColorManagement.fromWorkingColorSpace(toComponents(this,_rgb),colorSpace);return clamp(_rgb.r*255,0,255)<<16^clamp(_rgb.g*255,0,255)<<8^clamp(_rgb.b*255,0,255)<<0}getHexString(colorSpace=SRGBColorSpace){return("000000"+this.getHex(colorSpace).toString(16)).slice(-6)}getHSL(target,colorSpace=LinearSRGBColorSpace){ColorManagement.fromWorkingColorSpace(toComponents(this,_rgb),colorSpace);const r=_rgb.r,g=_rgb.g,b=_rgb.b;const max=Math.max(r,g,b);const min=Math.min(r,g,b);let hue,saturation;const lightness=(min+max)/2;if(min===max){hue=0;saturation=0}else{const delta=max-min;saturation=lightness<=.5?delta/(max+min):delta/(2-max-min);switch(max){case r:hue=(g-b)/delta+(g2048||canvas.height>2048){console.warn("THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons",image);return canvas.toDataURL("image/jpeg",.6)}else{return canvas.toDataURL("image/png")}}static sRGBToLinear(image){if(typeof HTMLImageElement!=="undefined"&&image instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&image instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&image instanceof ImageBitmap){const canvas=createElementNS("canvas");canvas.width=image.width;canvas.height=image.height;const context=canvas.getContext("2d");context.drawImage(image,0,0,image.width,image.height);const imageData=context.getImageData(0,0,image.width,image.height);const data=imageData.data;for(let i=0;i1){switch(this.wrapS){case RepeatWrapping:uv.x=uv.x-Math.floor(uv.x);break;case ClampToEdgeWrapping:uv.x=uv.x<0?0:1;break;case MirroredRepeatWrapping:if(Math.abs(Math.floor(uv.x)%2)===1){uv.x=Math.ceil(uv.x)-uv.x}else{uv.x=uv.x-Math.floor(uv.x)}break}}if(uv.y<0||uv.y>1){switch(this.wrapT){case RepeatWrapping:uv.y=uv.y-Math.floor(uv.y);break;case ClampToEdgeWrapping:uv.y=uv.y<0?0:1;break;case MirroredRepeatWrapping:if(Math.abs(Math.floor(uv.y)%2)===1){uv.y=Math.ceil(uv.y)-uv.y}else{uv.y=uv.y-Math.floor(uv.y)}break}}if(this.flipY){uv.y=1-uv.y}return uv}set needsUpdate(value){if(value===true){this.version++;this.source.needsUpdate=true}}}Texture.DEFAULT_IMAGE=null;Texture.DEFAULT_MAPPING=UVMapping;Texture.prototype.isTexture=true;class Vector4{constructor(x=0,y=0,z=0,w=1){this.x=x;this.y=y;this.z=z;this.w=w}get width(){return this.z}set width(value){this.z=value}get height(){return this.w}set height(value){this.w=value}set(x,y,z,w){this.x=x;this.y=y;this.z=z;this.w=w;return this}setScalar(scalar){this.x=scalar;this.y=scalar;this.z=scalar;this.w=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setZ(z){this.z=z;return this}setW(w){this.w=w;return this}setComponent(index,value){switch(index){case 0:this.x=value;break;case 1:this.y=value;break;case 2:this.z=value;break;case 3:this.w=value;break;default:throw new Error("index is out of range: "+index)}return this}getComponent(index){switch(index){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+index)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(v){this.x=v.x;this.y=v.y;this.z=v.z;this.w=v.w!==undefined?v.w:1;return this}add(v,w){if(w!==undefined){console.warn("THREE.Vector4: .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;this.w+=v.w;return this}addScalar(s){this.x+=s;this.y+=s;this.z+=s;this.w+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;this.z+=v.z*s;this.w+=v.w*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.Vector4: .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;this.w-=v.w;return this}subScalar(s){this.x-=s;this.y-=s;this.z-=s;this.w-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this}multiply(v){this.x*=v.x;this.y*=v.y;this.z*=v.z;this.w*=v.w;return this}multiplyScalar(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;this.w*=scalar;return this}applyMatrix4(m){const x=this.x,y=this.y,z=this.z,w=this.w;const e=m.elements;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;this.w=e[3]*x+e[7]*y+e[11]*z+e[15]*w;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}setAxisAngleFromQuaternion(q){this.w=2*Math.acos(q.w);const s=Math.sqrt(1-q.w*q.w);if(s<1e-4){this.x=1;this.y=0;this.z=0}else{this.x=q.x/s;this.y=q.y/s;this.z=q.z/s}return this}setAxisAngleFromRotationMatrix(m){let angle,x,y,z;const epsilon=.01,epsilon2=.1,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];if(Math.abs(m12-m21)yy&&xx>zz){if(xxzz){if(yy=0?1:-1,sqrSin=1-cos*cos;if(sqrSin>Number.EPSILON){const sin=Math.sqrt(sqrSin),len=Math.atan2(sin,cos*dir);s=Math.sin(s*len)/sin;t=Math.sin(t*len)/sin}const tDir=t*dir;x0=x0*s+x1*tDir;y0=y0*s+y1*tDir;z0=z0*s+z1*tDir;w0=w0*s+w1*tDir;if(s===1-t){const 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}static multiplyQuaternionsFlat(dst,dstOffset,src0,srcOffset0,src1,srcOffset1){const x0=src0[srcOffset0];const y0=src0[srcOffset0+1];const z0=src0[srcOffset0+2];const w0=src0[srcOffset0+3];const x1=src1[srcOffset1];const y1=src1[srcOffset1+1];const z1=src1[srcOffset1+2];const w1=src1[srcOffset1+3];dst[dstOffset]=x0*w1+w0*x1+y0*z1-z0*y1;dst[dstOffset+1]=y0*w1+w0*y1+z0*x1-x0*z1;dst[dstOffset+2]=z0*w1+w0*z1+x0*y1-y0*x1;dst[dstOffset+3]=w0*w1-x0*x1-y0*y1-z0*z1;return dst}get x(){return this._x}set x(value){this._x=value;this._onChangeCallback()}get y(){return this._y}set y(value){this._y=value;this._onChangeCallback()}get z(){return this._z}set z(value){this._z=value;this._onChangeCallback()}get w(){return this._w}set w(value){this._w=value;this._onChangeCallback()}set(x,y,z,w){this._x=x;this._y=y;this._z=z;this._w=w;this._onChangeCallback();return this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(quaternion){this._x=quaternion.x;this._y=quaternion.y;this._z=quaternion.z;this._w=quaternion.w;this._onChangeCallback();return this}setFromEuler(euler,update){if(!(euler&&euler.isEuler)){throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.")}const x=euler._x,y=euler._y,z=euler._z,order=euler._order;const cos=Math.cos;const sin=Math.sin;const c1=cos(x/2);const c2=cos(y/2);const c3=cos(z/2);const s1=sin(x/2);const s2=sin(y/2);const s3=sin(z/2);switch(order){case"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;break;case"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;break;case"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;break;case"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;break;case"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;break;case"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;break;default:console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: "+order)}if(update!==false)this._onChangeCallback();return this}setFromAxisAngle(axis,angle){const 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(m){const 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;if(trace>0){const s=.5/Math.sqrt(trace+1);this._w=.25/s;this._x=(m32-m23)*s;this._y=(m13-m31)*s;this._z=(m21-m12)*s}else if(m11>m22&&m11>m33){const s=2*Math.sqrt(1+m11-m22-m33);this._w=(m32-m23)/s;this._x=.25*s;this._y=(m12+m21)/s;this._z=(m13+m31)/s}else if(m22>m33){const s=2*Math.sqrt(1+m22-m11-m33);this._w=(m13-m31)/s;this._x=(m12+m21)/s;this._y=.25*s;this._z=(m23+m32)/s}else{const s=2*Math.sqrt(1+m33-m11-m22);this._w=(m21-m12)/s;this._x=(m13+m31)/s;this._y=(m23+m32)/s;this._z=.25*s}this._onChangeCallback();return this}setFromUnitVectors(vFrom,vTo){let r=vFrom.dot(vTo)+1;if(rMath.abs(vFrom.z)){this._x=-vFrom.y;this._y=vFrom.x;this._z=0;this._w=r}else{this._x=0;this._y=-vFrom.z;this._z=vFrom.y;this._w=r}}else{this._x=vFrom.y*vTo.z-vFrom.z*vTo.y;this._y=vFrom.z*vTo.x-vFrom.x*vTo.z;this._z=vFrom.x*vTo.y-vFrom.y*vTo.x;this._w=r}return this.normalize()}angleTo(q){return 2*Math.acos(Math.abs(clamp(this.dot(q),-1,1)))}rotateTowards(q,step){const angle=this.angleTo(q);if(angle===0)return this;const t=Math.min(1,step/angle);this.slerp(q,t);return this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){this._x*=-1;this._y*=-1;this._z*=-1;this._onChangeCallback();return this}dot(v){return this._x*v._x+this._y*v._y+this._z*v._z+this._w*v._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let 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(q,p){if(p!==undefined){console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.");return this.multiplyQuaternions(q,p)}return this.multiplyQuaternions(this,q)}premultiply(q){return this.multiplyQuaternions(q,this)}multiplyQuaternions(a,b){const qax=a._x,qay=a._y,qaz=a._z,qaw=a._w;const 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(qb,t){if(t===0)return this;if(t===1)return this.copy(qb);const x=this._x,y=this._y,z=this._z,w=this._w;let 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){this._w=w;this._x=x;this._y=y;this._z=z;return this}const sqrSinHalfTheta=1-cosHalfTheta*cosHalfTheta;if(sqrSinHalfTheta<=Number.EPSILON){const s=1-t;this._w=s*w+t*this._w;this._x=s*x+t*this._x;this._y=s*y+t*this._y;this._z=s*z+t*this._z;this.normalize();this._onChangeCallback();return this}const sinHalfTheta=Math.sqrt(sqrSinHalfTheta);const halfTheta=Math.atan2(sinHalfTheta,cosHalfTheta);const 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}slerpQuaternions(qa,qb,t){return this.copy(qa).slerp(qb,t)}random(){const u1=Math.random();const sqrt1u1=Math.sqrt(1-u1);const sqrtu1=Math.sqrt(u1);const u2=2*Math.PI*Math.random();const u3=2*Math.PI*Math.random();return this.set(sqrt1u1*Math.cos(u2),sqrtu1*Math.sin(u3),sqrtu1*Math.cos(u3),sqrt1u1*Math.sin(u2))}equals(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w}fromArray(array,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(array=[],offset=0){array[offset]=this._x;array[offset+1]=this._y;array[offset+2]=this._z;array[offset+3]=this._w;return array}fromBufferAttribute(attribute,index){this._x=attribute.getX(index);this._y=attribute.getY(index);this._z=attribute.getZ(index);this._w=attribute.getW(index);return this}_onChange(callback){this._onChangeCallback=callback;return this}_onChangeCallback(){}}Quaternion.prototype.isQuaternion=true;class Vector3{constructor(x=0,y=0,z=0){this.x=x;this.y=y;this.z=z}set(x,y,z){if(z===undefined)z=this.z;this.x=x;this.y=y;this.z=z;return this}setScalar(scalar){this.x=scalar;this.y=scalar;this.z=scalar;return this}setX(x){this.x=x;return this}setY(y){this.y=y;return this}setZ(z){this.z=z;return this}setComponent(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(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(){return new this.constructor(this.x,this.y,this.z)}copy(v){this.x=v.x;this.y=v.y;this.z=v.z;return this}add(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x+=s;this.y+=s;this.z+=s;return this}addVectors(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this}addScaledVector(v,s){this.x+=v.x*s;this.y+=v.y*s;this.z+=v.z*s;return this}sub(v,w){if(w!==undefined){console.warn("THREE.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(s){this.x-=s;this.y-=s;this.z-=s;return this}subVectors(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this}multiply(v,w){if(w!==undefined){console.warn("THREE.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(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;return this}multiplyVectors(a,b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this}applyEuler(euler){if(!(euler&&euler.isEuler)){console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.")}return this.applyQuaternion(_quaternion$4.setFromEuler(euler))}applyAxisAngle(axis,angle){return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis,angle))}applyMatrix3(m){const x=this.x,y=this.y,z=this.z;const 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}applyNormalMatrix(m){return this.applyMatrix3(m).normalize()}applyMatrix4(m){const x=this.x,y=this.y,z=this.z;const e=m.elements;const 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(q){const x=this.x,y=this.y,z=this.z;const qx=q.x,qy=q.y,qz=q.z,qw=q.w;const ix=qw*x+qy*z-qz*y;const iy=qw*y+qz*x-qx*z;const iz=qw*z+qx*y-qy*x;const iw=-qx*x-qy*y-qz*z;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(camera){return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix)}unproject(camera){return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld)}transformDirection(m){const x=this.x,y=this.y,z=this.z;const 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(v){this.x/=v.x;this.y/=v.y;this.z/=v.z;return this}divideScalar(scalar){return this.multiplyScalar(1/scalar)}min(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(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(min,max){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(minVal,maxVal){this.x=Math.max(minVal,Math.min(maxVal,this.x));this.y=Math.max(minVal,Math.min(maxVal,this.y));this.z=Math.max(minVal,Math.min(maxVal,this.z));return this}clampLength(min,max){const length=this.length();return this.divideScalar(length||1).multiplyScalar(Math.max(min,Math.min(max,length)))}floor(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this}ceil(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this}round(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this}roundToZero(){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(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this}dot(v){return this.x*v.x+this.y*v.y+this.z*v.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(length){return this.normalize().multiplyScalar(length)}lerp(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(v1,v2,alpha){this.x=v1.x+(v2.x-v1.x)*alpha;this.y=v1.y+(v2.y-v1.y)*alpha;this.z=v1.z+(v2.z-v1.z)*alpha;return this}cross(v,w){if(w!==undefined){console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.");return this.crossVectors(v,w)}return this.crossVectors(this,v)}crossVectors(a,b){const ax=a.x,ay=a.y,az=a.z;const 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(v){const denominator=v.lengthSq();if(denominator===0)return this.set(0,0,0);const scalar=v.dot(this)/denominator;return this.copy(v).multiplyScalar(scalar)}projectOnPlane(planeNormal){_vector$c.copy(this).projectOnVector(planeNormal);return this.sub(_vector$c)}reflect(normal){return this.sub(_vector$c.copy(normal).multiplyScalar(2*this.dot(normal)))}angleTo(v){const denominator=Math.sqrt(this.lengthSq()*v.lengthSq());if(denominator===0)return Math.PI/2;const theta=this.dot(v)/denominator;return Math.acos(clamp(theta,-1,1))}distanceTo(v){return Math.sqrt(this.distanceToSquared(v))}distanceToSquared(v){const dx=this.x-v.x,dy=this.y-v.y,dz=this.z-v.z;return dx*dx+dy*dy+dz*dz}manhattanDistanceTo(v){return Math.abs(this.x-v.x)+Math.abs(this.y-v.y)+Math.abs(this.z-v.z)}setFromSpherical(s){return this.setFromSphericalCoords(s.radius,s.phi,s.theta)}setFromSphericalCoords(radius,phi,theta){const sinPhiRadius=Math.sin(phi)*radius;this.x=sinPhiRadius*Math.sin(theta);this.y=Math.cos(phi)*radius;this.z=sinPhiRadius*Math.cos(theta);return this}setFromCylindrical(c){return this.setFromCylindricalCoords(c.radius,c.theta,c.y)}setFromCylindricalCoords(radius,theta,y){this.x=radius*Math.sin(theta);this.y=y;this.z=radius*Math.cos(theta);return this}setFromMatrixPosition(m){const e=m.elements;this.x=e[12];this.y=e[13];this.z=e[14];return this}setFromMatrixScale(m){const sx=this.setFromMatrixColumn(m,0).length();const sy=this.setFromMatrixColumn(m,1).length();const sz=this.setFromMatrixColumn(m,2).length();this.x=sx;this.y=sy;this.z=sz;return this}setFromMatrixColumn(m,index){return this.fromArray(m.elements,index*4)}setFromMatrix3Column(m,index){return this.fromArray(m.elements,index*3)}setFromEuler(e){this.x=e._x;this.y=e._y;this.z=e._z;return this}equals(v){return v.x===this.x&&v.y===this.y&&v.z===this.z}fromArray(array,offset=0){this.x=array[offset];this.y=array[offset+1];this.z=array[offset+2];return this}toArray(array=[],offset=0){array[offset]=this.x;array[offset+1]=this.y;array[offset+2]=this.z;return array}fromBufferAttribute(attribute,index,offset){if(offset!==undefined){console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute().")}this.x=attribute.getX(index);this.y=attribute.getY(index);this.z=attribute.getZ(index);return this}random(){this.x=Math.random();this.y=Math.random();this.z=Math.random();return this}randomDirection(){const u=(Math.random()-.5)*2;const t=Math.random()*Math.PI*2;const f=Math.sqrt(1-u**2);this.x=f*Math.cos(t);this.y=f*Math.sin(t);this.z=u;return this}*[Symbol.iterator](){yield this.x;yield this.y;yield this.z}}Vector3.prototype.isVector3=true;const _vector$c=new Vector3;const _quaternion$4=new Quaternion;class Box3{constructor(min=new Vector3(+Infinity,+Infinity,+Infinity),max=new Vector3(-Infinity,-Infinity,-Infinity)){this.min=min;this.max=max}set(min,max){this.min.copy(min);this.max.copy(max);return this}setFromArray(array){let minX=+Infinity;let minY=+Infinity;let minZ=+Infinity;let maxX=-Infinity;let maxY=-Infinity;let maxZ=-Infinity;for(let i=0,l=array.length;imaxX)maxX=x;if(y>maxY)maxY=y;if(z>maxZ)maxZ=z}this.min.set(minX,minY,minZ);this.max.set(maxX,maxY,maxZ);return this}setFromBufferAttribute(attribute){let minX=+Infinity;let minY=+Infinity;let minZ=+Infinity;let maxX=-Infinity;let maxY=-Infinity;let maxZ=-Infinity;for(let i=0,l=attribute.count;imaxX)maxX=x;if(y>maxY)maxY=y;if(z>maxZ)maxZ=z}this.min.set(minX,minY,minZ);this.max.set(maxX,maxY,maxZ);return this}setFromPoints(points){this.makeEmpty();for(let i=0,il=points.length;ithis.max.x||point.ythis.max.y||point.zthis.max.z?false:true}containsBox(box){return this.min.x<=box.min.x&&box.max.x<=this.max.x&&this.min.y<=box.min.y&&box.max.y<=this.max.y&&this.min.z<=box.min.z&&box.max.z<=this.max.z}getParameter(point,target){return target.set((point.x-this.min.x)/(this.max.x-this.min.x),(point.y-this.min.y)/(this.max.y-this.min.y),(point.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(box){return box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z?false:true}intersectsSphere(sphere){this.clampPoint(sphere.center,_vector$b);return _vector$b.distanceToSquared(sphere.center)<=sphere.radius*sphere.radius}intersectsPlane(plane){let min,max;if(plane.normal.x>0){min=plane.normal.x*this.min.x;max=plane.normal.x*this.max.x}else{min=plane.normal.x*this.max.x;max=plane.normal.x*this.min.x}if(plane.normal.y>0){min+=plane.normal.y*this.min.y;max+=plane.normal.y*this.max.y}else{min+=plane.normal.y*this.max.y;max+=plane.normal.y*this.min.y}if(plane.normal.z>0){min+=plane.normal.z*this.min.z;max+=plane.normal.z*this.max.z}else{min+=plane.normal.z*this.max.z;max+=plane.normal.z*this.min.z}return min<=-plane.constant&&max>=-plane.constant}intersectsTriangle(triangle){if(this.isEmpty()){return false}this.getCenter(_center);_extents.subVectors(this.max,_center);_v0$2.subVectors(triangle.a,_center);_v1$7.subVectors(triangle.b,_center);_v2$3.subVectors(triangle.c,_center);_f0.subVectors(_v1$7,_v0$2);_f1.subVectors(_v2$3,_v1$7);_f2.subVectors(_v0$2,_v2$3);let axes=[0,-_f0.z,_f0.y,0,-_f1.z,_f1.y,0,-_f2.z,_f2.y,_f0.z,0,-_f0.x,_f1.z,0,-_f1.x,_f2.z,0,-_f2.x,-_f0.y,_f0.x,0,-_f1.y,_f1.x,0,-_f2.y,_f2.x,0];if(!satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)){return false}axes=[1,0,0,0,1,0,0,0,1];if(!satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)){return false}_triangleNormal.crossVectors(_f0,_f1);axes=[_triangleNormal.x,_triangleNormal.y,_triangleNormal.z];return satForAxes(axes,_v0$2,_v1$7,_v2$3,_extents)}clampPoint(point,target){return target.copy(point).clamp(this.min,this.max)}distanceToPoint(point){const clampedPoint=_vector$b.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length()}getBoundingSphere(target){this.getCenter(target.center);target.radius=this.getSize(_vector$b).length()*.5;return target}intersect(box){this.min.max(box.min);this.max.min(box.max);if(this.isEmpty())this.makeEmpty();return this}union(box){this.min.min(box.min);this.max.max(box.max);return this}applyMatrix4(matrix){if(this.isEmpty())return this;_points[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(matrix);_points[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(matrix);_points[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(matrix);_points[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(matrix);_points[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(matrix);_points[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(matrix);_points[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(matrix);_points[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(matrix);this.setFromPoints(_points);return this}translate(offset){this.min.add(offset);this.max.add(offset);return this}equals(box){return box.min.equals(this.min)&&box.max.equals(this.max)}}Box3.prototype.isBox3=true;const _points=[new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3,new Vector3];const _vector$b=new Vector3;const _box$3=new Box3;const _v0$2=new Vector3;const _v1$7=new Vector3;const _v2$3=new Vector3;const _f0=new Vector3;const _f1=new Vector3;const _f2=new Vector3;const _center=new Vector3;const _extents=new Vector3;const _triangleNormal=new Vector3;const _testAxis=new Vector3;function satForAxes(axes,v0,v1,v2,extents){for(let i=0,j=axes.length-3;i<=j;i+=3){_testAxis.fromArray(axes,i);const r=extents.x*Math.abs(_testAxis.x)+extents.y*Math.abs(_testAxis.y)+extents.z*Math.abs(_testAxis.z);const p0=v0.dot(_testAxis);const p1=v1.dot(_testAxis);const p2=v2.dot(_testAxis);if(Math.max(-Math.max(p0,p1,p2),Math.min(p0,p1,p2))>r){return false}}return true}const _box$2=new Box3;const _v1$6=new Vector3;const _toFarthestPoint=new Vector3;const _toPoint=new Vector3;class Sphere{constructor(center=new Vector3,radius=-1){this.center=center;this.radius=radius}set(center,radius){this.center.copy(center);this.radius=radius;return this}setFromPoints(points,optionalCenter){const center=this.center;if(optionalCenter!==undefined){center.copy(optionalCenter)}else{_box$2.setFromPoints(points).getCenter(center)}let maxRadiusSq=0;for(let i=0,il=points.length;ithis.radius*this.radius){target.sub(this.center).normalize();target.multiplyScalar(this.radius).add(this.center)}return target}getBoundingBox(target){if(this.isEmpty()){target.makeEmpty();return target}target.set(this.center,this.center);target.expandByScalar(this.radius);return target}applyMatrix4(matrix){this.center.applyMatrix4(matrix);this.radius=this.radius*matrix.getMaxScaleOnAxis();return this}translate(offset){this.center.add(offset);return this}expandByPoint(point){_toPoint.subVectors(point,this.center);const lengthSq=_toPoint.lengthSq();if(lengthSq>this.radius*this.radius){const length=Math.sqrt(lengthSq);const missingRadiusHalf=(length-this.radius)*.5;this.center.add(_toPoint.multiplyScalar(missingRadiusHalf/length));this.radius+=missingRadiusHalf}return this}union(sphere){if(this.center.equals(sphere.center)===true){_toFarthestPoint.set(0,0,1).multiplyScalar(sphere.radius)}else{_toFarthestPoint.subVectors(sphere.center,this.center).normalize().multiplyScalar(sphere.radius)}this.expandByPoint(_v1$6.copy(sphere.center).add(_toFarthestPoint));this.expandByPoint(_v1$6.copy(sphere.center).sub(_toFarthestPoint));return this}equals(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius}clone(){return(new this.constructor).copy(this)}}const _vector$a=new Vector3;const _segCenter=new Vector3;const _segDir=new Vector3;const _diff=new Vector3;const _edge1=new Vector3;const _edge2=new Vector3;const _normal$1=new Vector3;class Ray{constructor(origin=new Vector3,direction=new Vector3(0,0,-1)){this.origin=origin;this.direction=direction}set(origin,direction){this.origin.copy(origin);this.direction.copy(direction);return this}copy(ray){this.origin.copy(ray.origin);this.direction.copy(ray.direction);return this}at(t,target){return target.copy(this.direction).multiplyScalar(t).add(this.origin)}lookAt(v){this.direction.copy(v).sub(this.origin).normalize();return this}recast(t){this.origin.copy(this.at(t,_vector$a));return this}closestPointToPoint(point,target){target.subVectors(point,this.origin);const directionDistance=target.dot(this.direction);if(directionDistance<0){return target.copy(this.origin)}return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin)}distanceToPoint(point){return Math.sqrt(this.distanceSqToPoint(point))}distanceSqToPoint(point){const directionDistance=_vector$a.subVectors(point,this.origin).dot(this.direction);if(directionDistance<0){return this.origin.distanceToSquared(point)}_vector$a.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);return _vector$a.distanceToSquared(point)}distanceSqToSegment(v0,v1,optionalPointOnRay,optionalPointOnSegment){_segCenter.copy(v0).add(v1).multiplyScalar(.5);_segDir.copy(v1).sub(v0).normalize();_diff.copy(this.origin).sub(_segCenter);const segExtent=v0.distanceTo(v1)*.5;const a01=-this.direction.dot(_segDir);const b0=_diff.dot(this.direction);const b1=-_diff.dot(_segDir);const c=_diff.lengthSq();const det=Math.abs(1-a01*a01);let s0,s1,sqrDist,extDet;if(det>0){s0=a01*b1-b0;s1=a01*b0-b1;extDet=segExtent*det;if(s0>=0){if(s1>=-extDet){if(s1<=extDet){const invDet=1/det;s0*=invDet;s1*=invDet;sqrDist=s0*(s0+a01*s1+2*b0)+s1*(a01*s0+s1+2*b1)+c}else{s1=segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}}else{s1=-segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}}else{if(s1<=-extDet){s0=Math.max(0,-(-a01*segExtent+b0));s1=s0>0?-segExtent:Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=-s0*s0+s1*(s1+2*b1)+c}else if(s1<=extDet){s0=0;s1=Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=s1*(s1+2*b1)+c}else{s0=Math.max(0,-(a01*segExtent+b0));s1=s0>0?segExtent:Math.min(Math.max(-segExtent,-b1),segExtent);sqrDist=-s0*s0+s1*(s1+2*b1)+c}}}else{s1=a01>0?-segExtent:segExtent;s0=Math.max(0,-(a01*s1+b0));sqrDist=-s0*s0+s1*(s1+2*b1)+c}if(optionalPointOnRay){optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin)}if(optionalPointOnSegment){optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter)}return sqrDist}intersectSphere(sphere,target){_vector$a.subVectors(sphere.center,this.origin);const tca=_vector$a.dot(this.direction);const d2=_vector$a.dot(_vector$a)-tca*tca;const radius2=sphere.radius*sphere.radius;if(d2>radius2)return null;const thc=Math.sqrt(radius2-d2);const t0=tca-thc;const t1=tca+thc;if(t0<0&&t1<0)return null;if(t0<0)return this.at(t1,target);return this.at(t0,target)}intersectsSphere(sphere){return this.distanceSqToPoint(sphere.center)<=sphere.radius*sphere.radius}distanceToPlane(plane){const denominator=plane.normal.dot(this.direction);if(denominator===0){if(plane.distanceToPoint(this.origin)===0){return 0}return null}const t=-(this.origin.dot(plane.normal)+plane.constant)/denominator;return t>=0?t:null}intersectPlane(plane,target){const t=this.distanceToPlane(plane);if(t===null){return null}return this.at(t,target)}intersectsPlane(plane){const distToPoint=plane.distanceToPoint(this.origin);if(distToPoint===0){return true}const denominator=plane.normal.dot(this.direction);if(denominator*distToPoint<0){return true}return false}intersectBox(box,target){let tmin,tmax,tymin,tymax,tzmin,tzmax;const invdirx=1/this.direction.x,invdiry=1/this.direction.y,invdirz=1/this.direction.z;const origin=this.origin;if(invdirx>=0){tmin=(box.min.x-origin.x)*invdirx;tmax=(box.max.x-origin.x)*invdirx}else{tmin=(box.max.x-origin.x)*invdirx;tmax=(box.min.x-origin.x)*invdirx}if(invdiry>=0){tymin=(box.min.y-origin.y)*invdiry;tymax=(box.max.y-origin.y)*invdiry}else{tymin=(box.max.y-origin.y)*invdiry;tymax=(box.min.y-origin.y)*invdiry}if(tmin>tymax||tymin>tmax)return null;if(tymin>tmin||tmin!==tmin)tmin=tymin;if(tymax=0){tzmin=(box.min.z-origin.z)*invdirz;tzmax=(box.max.z-origin.z)*invdirz}else{tzmin=(box.max.z-origin.z)*invdirz;tzmax=(box.min.z-origin.z)*invdirz}if(tmin>tzmax||tzmin>tmax)return null;if(tzmin>tmin||tmin!==tmin)tmin=tzmin;if(tzmax=0?tmin:tmax,target)}intersectsBox(box){return this.intersectBox(box,_vector$a)!==null}intersectTriangle(a,b,c,backfaceCulling,target){_edge1.subVectors(b,a);_edge2.subVectors(c,a);_normal$1.crossVectors(_edge1,_edge2);let DdN=this.direction.dot(_normal$1);let sign;if(DdN>0){if(backfaceCulling)return null;sign=1}else if(DdN<0){sign=-1;DdN=-DdN}else{return null}_diff.subVectors(this.origin,a);const DdQxE2=sign*this.direction.dot(_edge2.crossVectors(_diff,_edge2));if(DdQxE2<0){return null}const DdE1xQ=sign*this.direction.dot(_edge1.cross(_diff));if(DdE1xQ<0){return null}if(DdQxE2+DdE1xQ>DdN){return null}const QdN=-sign*_diff.dot(_normal$1);if(QdN<0){return null}return this.at(QdN/DdN,target)}applyMatrix4(matrix4){this.origin.applyMatrix4(matrix4);this.direction.transformDirection(matrix4);return this}equals(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction)}clone(){return(new this.constructor).copy(this)}}class Matrix4{constructor(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];if(arguments.length>0){console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")}}set(n11,n12,n13,n14,n21,n22,n23,n24,n31,n32,n33,n34,n41,n42,n43,n44){const 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(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this}clone(){return(new Matrix4).fromArray(this.elements)}copy(m){const te=this.elements;const 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(m){const te=this.elements,me=m.elements;te[12]=me[12];te[13]=me[13];te[14]=me[14];return this}setFromMatrix3(m){const me=m.elements;this.set(me[0],me[3],me[6],0,me[1],me[4],me[7],0,me[2],me[5],me[8],0,0,0,0,1);return this}extractBasis(xAxis,yAxis,zAxis){xAxis.setFromMatrixColumn(this,0);yAxis.setFromMatrixColumn(this,1);zAxis.setFromMatrixColumn(this,2);return this}makeBasis(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(m){const te=this.elements;const me=m.elements;const scaleX=1/_v1$5.setFromMatrixColumn(m,0).length();const scaleY=1/_v1$5.setFromMatrixColumn(m,1).length();const scaleZ=1/_v1$5.setFromMatrixColumn(m,2).length();te[0]=me[0]*scaleX;te[1]=me[1]*scaleX;te[2]=me[2]*scaleX;te[3]=0;te[4]=me[4]*scaleY;te[5]=me[5]*scaleY;te[6]=me[6]*scaleY;te[7]=0;te[8]=me[8]*scaleZ;te[9]=me[9]*scaleZ;te[10]=me[10]*scaleZ;te[11]=0;te[12]=0;te[13]=0;te[14]=0;te[15]=1;return this}makeRotationFromEuler(euler){if(!(euler&&euler.isEuler)){console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.")}const te=this.elements;const x=euler.x,y=euler.y,z=euler.z;const a=Math.cos(x),b=Math.sin(x);const c=Math.cos(y),d=Math.sin(y);const e=Math.cos(z),f=Math.sin(z);if(euler.order==="XYZ"){const 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"){const 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"){const 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"){const 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"){const 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"){const 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}te[3]=0;te[7]=0;te[11]=0;te[12]=0;te[13]=0;te[14]=0;te[15]=1;return this}makeRotationFromQuaternion(q){return this.compose(_zero,q,_one)}lookAt(eye,target,up){const te=this.elements;_z.subVectors(eye,target);if(_z.lengthSq()===0){_z.z=1}_z.normalize();_x.crossVectors(up,_z);if(_x.lengthSq()===0){if(Math.abs(up.z)===1){_z.x+=1e-4}else{_z.z+=1e-4}_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(m,n){if(n!==undefined){console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.");return this.multiplyMatrices(m,n)}return this.multiplyMatrices(this,m)}premultiply(m){return this.multiplyMatrices(m,this)}multiplyMatrices(a,b){const ae=a.elements;const be=b.elements;const te=this.elements;const a11=ae[0],a12=ae[4],a13=ae[8],a14=ae[12];const a21=ae[1],a22=ae[5],a23=ae[9],a24=ae[13];const a31=ae[2],a32=ae[6],a33=ae[10],a34=ae[14];const a41=ae[3],a42=ae[7],a43=ae[11],a44=ae[15];const b11=be[0],b12=be[4],b13=be[8],b14=be[12];const b21=be[1],b22=be[5],b23=be[9],b24=be[13];const b31=be[2],b32=be[6],b33=be[10],b34=be[14];const 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(s){const 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}determinant(){const te=this.elements;const n11=te[0],n12=te[4],n13=te[8],n14=te[12];const n21=te[1],n22=te[5],n23=te[9],n24=te[13];const n31=te[2],n32=te[6],n33=te[10],n34=te[14];const n41=te[3],n42=te[7],n43=te[11],n44=te[15];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(){const te=this.elements;let 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(x,y,z){const te=this.elements;if(x.isVector3){te[12]=x.x;te[13]=x.y;te[14]=x.z}else{te[12]=x;te[13]=y;te[14]=z}return this}invert(){const te=this.elements,n11=te[0],n21=te[1],n31=te[2],n41=te[3],n12=te[4],n22=te[5],n32=te[6],n42=te[7],n13=te[8],n23=te[9],n33=te[10],n43=te[11],n14=te[12],n24=te[13],n34=te[14],n44=te[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;const det=n11*t11+n21*t12+n31*t13+n41*t14;if(det===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const 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(v){const te=this.elements;const 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(){const te=this.elements;const scaleXSq=te[0]*te[0]+te[1]*te[1]+te[2]*te[2];const scaleYSq=te[4]*te[4]+te[5]*te[5]+te[6]*te[6];const scaleZSq=te[8]*te[8]+te[9]*te[9]+te[10]*te[10];return Math.sqrt(Math.max(scaleXSq,scaleYSq,scaleZSq))}makeTranslation(x,y,z){this.set(1,0,0,x,0,1,0,y,0,0,1,z,0,0,0,1);return this}makeRotationX(theta){const 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(theta){const 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(theta){const 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(axis,angle){const c=Math.cos(angle);const s=Math.sin(angle);const t=1-c;const x=axis.x,y=axis.y,z=axis.z;const 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(x,y,z){this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1);return this}makeShear(xy,xz,yx,yz,zx,zy){this.set(1,yx,zx,0,xy,1,zy,0,xz,yz,1,0,0,0,0,1);return this}compose(position,quaternion,scale){const te=this.elements;const x=quaternion._x,y=quaternion._y,z=quaternion._z,w=quaternion._w;const x2=x+x,y2=y+y,z2=z+z;const xx=x*x2,xy=x*y2,xz=x*z2;const yy=y*y2,yz=y*z2,zz=z*z2;const wx=w*x2,wy=w*y2,wz=w*z2;const sx=scale.x,sy=scale.y,sz=scale.z;te[0]=(1-(yy+zz))*sx;te[1]=(xy+wz)*sx;te[2]=(xz-wy)*sx;te[3]=0;te[4]=(xy-wz)*sy;te[5]=(1-(xx+zz))*sy;te[6]=(yz+wx)*sy;te[7]=0;te[8]=(xz+wy)*sz;te[9]=(yz-wx)*sz;te[10]=(1-(xx+yy))*sz;te[11]=0;te[12]=position.x;te[13]=position.y;te[14]=position.z;te[15]=1;return this}decompose(position,quaternion,scale){const te=this.elements;let sx=_v1$5.set(te[0],te[1],te[2]).length();const sy=_v1$5.set(te[4],te[5],te[6]).length();const sz=_v1$5.set(te[8],te[9],te[10]).length();const det=this.determinant();if(det<0)sx=-sx;position.x=te[12];position.y=te[13];position.z=te[14];_m1$2.copy(this);const invSX=1/sx;const invSY=1/sy;const invSZ=1/sz;_m1$2.elements[0]*=invSX;_m1$2.elements[1]*=invSX;_m1$2.elements[2]*=invSX;_m1$2.elements[4]*=invSY;_m1$2.elements[5]*=invSY;_m1$2.elements[6]*=invSY;_m1$2.elements[8]*=invSZ;_m1$2.elements[9]*=invSZ;_m1$2.elements[10]*=invSZ;quaternion.setFromRotationMatrix(_m1$2);scale.x=sx;scale.y=sy;scale.z=sz;return this}makePerspective(left,right,top,bottom,near,far){if(far===undefined){console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.")}const te=this.elements;const x=2*near/(right-left);const y=2*near/(top-bottom);const a=(right+left)/(right-left);const b=(top+bottom)/(top-bottom);const c=-(far+near)/(far-near);const 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(left,right,top,bottom,near,far){const te=this.elements;const w=1/(right-left);const h=1/(top-bottom);const p=1/(far-near);const x=(right+left)*w;const y=(top+bottom)*h;const 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(matrix){const te=this.elements;const me=matrix.elements;for(let i=0;i<16;i++){if(te[i]!==me[i])return false}return true}fromArray(array,offset=0){for(let i=0;i<16;i++){this.elements[i]=array[i+offset]}return this}toArray(array=[],offset=0){const 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}}Matrix4.prototype.isMatrix4=true;const _v1$5=new Vector3;const _m1$2=new Matrix4;const _zero=new Vector3(0,0,0);const _one=new Vector3(1,1,1);const _x=new Vector3;const _y=new Vector3;const _z=new Vector3;const _matrix$1=new Matrix4;const _quaternion$3=new Quaternion;class Euler{constructor(x=0,y=0,z=0,order=Euler.DefaultOrder){this._x=x;this._y=y;this._z=z;this._order=order}get x(){return this._x}set x(value){this._x=value;this._onChangeCallback()}get y(){return this._y}set y(value){this._y=value;this._onChangeCallback()}get z(){return this._z}set z(value){this._z=value;this._onChangeCallback()}get order(){return this._order}set order(value){this._order=value;this._onChangeCallback()}set(x,y,z,order=this._order){this._x=x;this._y=y;this._z=z;this._order=order;this._onChangeCallback();return this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(euler){this._x=euler._x;this._y=euler._y;this._z=euler._z;this._order=euler._order;this._onChangeCallback();return this}setFromRotationMatrix(m,order=this._order,update=true){const te=m.elements;const m11=te[0],m12=te[4],m13=te[8];const m21=te[1],m22=te[5],m23=te[9];const m31=te[2],m32=te[6],m33=te[10];switch(order){case"XYZ":this._y=Math.asin(clamp(m13,-1,1));if(Math.abs(m13)<.9999999){this._x=Math.atan2(-m23,m33);this._z=Math.atan2(-m12,m11)}else{this._x=Math.atan2(m32,m22);this._z=0}break;case"YXZ":this._x=Math.asin(-clamp(m23,-1,1));if(Math.abs(m23)<.9999999){this._y=Math.atan2(m13,m33);this._z=Math.atan2(m21,m22)}else{this._y=Math.atan2(-m31,m11);this._z=0}break;case"ZXY":this._x=Math.asin(clamp(m32,-1,1));if(Math.abs(m32)<.9999999){this._y=Math.atan2(-m31,m33);this._z=Math.atan2(-m12,m22)}else{this._y=0;this._z=Math.atan2(m21,m11)}break;case"ZYX":this._y=Math.asin(-clamp(m31,-1,1));if(Math.abs(m31)<.9999999){this._x=Math.atan2(m32,m33);this._z=Math.atan2(m21,m11)}else{this._x=0;this._z=Math.atan2(-m12,m22)}break;case"YZX":this._z=Math.asin(clamp(m21,-1,1));if(Math.abs(m21)<.9999999){this._x=Math.atan2(-m23,m22);this._y=Math.atan2(-m31,m11)}else{this._x=0;this._y=Math.atan2(m13,m33)}break;case"XZY":this._z=Math.asin(-clamp(m12,-1,1));if(Math.abs(m12)<.9999999){this._x=Math.atan2(m32,m22);this._y=Math.atan2(m13,m11)}else{this._x=Math.atan2(-m23,m33);this._y=0}break;default:console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: "+order)}this._order=order;if(update===true)this._onChangeCallback();return this}setFromQuaternion(q,order,update){_matrix$1.makeRotationFromQuaternion(q);return this.setFromRotationMatrix(_matrix$1,order,update)}setFromVector3(v,order=this._order){return this.set(v.x,v.y,v.z,order)}reorder(newOrder){_quaternion$3.setFromEuler(this);return this.setFromQuaternion(_quaternion$3,newOrder)}equals(euler){return euler._x===this._x&&euler._y===this._y&&euler._z===this._z&&euler._order===this._order}fromArray(array){this._x=array[0];this._y=array[1];this._z=array[2];if(array[3]!==undefined)this._order=array[3];this._onChangeCallback();return this}toArray(array=[],offset=0){array[offset]=this._x;array[offset+1]=this._y;array[offset+2]=this._z;array[offset+3]=this._order;return array}_onChange(callback){this._onChangeCallback=callback;return this}_onChangeCallback(){}}Euler.prototype.isEuler=true;Euler.DefaultOrder="XYZ";Euler.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];class Layers{constructor(){this.mask=1|0}set(channel){this.mask=(1<>>0}enable(channel){this.mask|=1<1){for(let i=0;i1){for(let i=0;i0){object.children=[];for(let i=0;i0){object.animations=[];for(let i=0;i0)output.geometries=geometries;if(materials.length>0)output.materials=materials;if(textures.length>0)output.textures=textures;if(images.length>0)output.images=images;if(shapes.length>0)output.shapes=shapes;if(skeletons.length>0)output.skeletons=skeletons;if(animations.length>0)output.animations=animations;if(nodes.length>0)output.nodes=nodes}output.object=object;return output;function extractFromCache(cache){const values=[];for(const key in cache){const data=cache[key];delete data.metadata;values.push(data)}return values}}clone(recursive){return(new this.constructor).copy(this,recursive)}copy(source,recursive=true){this.name=source.name;this.up.copy(source.up);this.position.copy(source.position);this.rotation.order=source.rotation.order;this.quaternion.copy(source.quaternion);this.scale.copy(source.scale);this.matrix.copy(source.matrix);this.matrixWorld.copy(source.matrixWorld);this.matrixAutoUpdate=source.matrixAutoUpdate;this.matrixWorldNeedsUpdate=source.matrixWorldNeedsUpdate;this.layers.mask=source.layers.mask;this.visible=source.visible;this.castShadow=source.castShadow;this.receiveShadow=source.receiveShadow;this.frustumCulled=source.frustumCulled;this.renderOrder=source.renderOrder;this.userData=JSON.parse(JSON.stringify(source.userData));if(recursive===true){for(let i=0;i0){return target.multiplyScalar(1/Math.sqrt(targetLengthSq))}return target.set(0,0,0)}static getBarycoord(point,a,b,c,target){_v0$1.subVectors(c,a);_v1$3.subVectors(b,a);_v2$2.subVectors(point,a);const dot00=_v0$1.dot(_v0$1);const dot01=_v0$1.dot(_v1$3);const dot02=_v0$1.dot(_v2$2);const dot11=_v1$3.dot(_v1$3);const dot12=_v1$3.dot(_v2$2);const denom=dot00*dot11-dot01*dot01;if(denom===0){return target.set(-2,-1,-1)}const invDenom=1/denom;const u=(dot11*dot02-dot01*dot12)*invDenom;const v=(dot00*dot12-dot01*dot02)*invDenom;return target.set(1-u-v,v,u)}static containsPoint(point,a,b,c){this.getBarycoord(point,a,b,c,_v3$1);return _v3$1.x>=0&&_v3$1.y>=0&&_v3$1.x+_v3$1.y<=1}static getUV(point,p1,p2,p3,uv1,uv2,uv3,target){this.getBarycoord(point,p1,p2,p3,_v3$1);target.set(0,0);target.addScaledVector(uv1,_v3$1.x);target.addScaledVector(uv2,_v3$1.y);target.addScaledVector(uv3,_v3$1.z);return target}static isFrontFacing(a,b,c,direction){_v0$1.subVectors(c,b);_v1$3.subVectors(a,b);return _v0$1.cross(_v1$3).dot(direction)<0?true:false}set(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this}setFromPointsAndIndices(points,i0,i1,i2){this.a.copy(points[i0]);this.b.copy(points[i1]);this.c.copy(points[i2]);return this}setFromAttributeAndIndices(attribute,i0,i1,i2){this.a.fromBufferAttribute(attribute,i0);this.b.fromBufferAttribute(attribute,i1);this.c.fromBufferAttribute(attribute,i2);return this}clone(){return(new this.constructor).copy(this)}copy(triangle){this.a.copy(triangle.a);this.b.copy(triangle.b);this.c.copy(triangle.c);return this}getArea(){_v0$1.subVectors(this.c,this.b);_v1$3.subVectors(this.a,this.b);return _v0$1.cross(_v1$3).length()*.5}getMidpoint(target){return target.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(target){return Triangle.getNormal(this.a,this.b,this.c,target)}getPlane(target){return target.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(point,target){return Triangle.getBarycoord(point,this.a,this.b,this.c,target)}getUV(point,uv1,uv2,uv3,target){return Triangle.getUV(point,this.a,this.b,this.c,uv1,uv2,uv3,target)}containsPoint(point){return Triangle.containsPoint(point,this.a,this.b,this.c)}isFrontFacing(direction){return Triangle.isFrontFacing(this.a,this.b,this.c,direction)}intersectsBox(box){return box.intersectsTriangle(this)}closestPointToPoint(p,target){const a=this.a,b=this.b,c=this.c;let v,w;_vab.subVectors(b,a);_vac.subVectors(c,a);_vap.subVectors(p,a);const d1=_vab.dot(_vap);const d2=_vac.dot(_vap);if(d1<=0&&d2<=0){return target.copy(a)}_vbp.subVectors(p,b);const d3=_vab.dot(_vbp);const d4=_vac.dot(_vbp);if(d3>=0&&d4<=d3){return target.copy(b)}const vc=d1*d4-d3*d2;if(vc<=0&&d1>=0&&d3<=0){v=d1/(d1-d3);return target.copy(a).addScaledVector(_vab,v)}_vcp.subVectors(p,c);const d5=_vab.dot(_vcp);const d6=_vac.dot(_vcp);if(d6>=0&&d5<=d6){return target.copy(c)}const vb=d5*d2-d1*d6;if(vb<=0&&d2>=0&&d6<=0){w=d2/(d2-d6);return target.copy(a).addScaledVector(_vac,w)}const va=d3*d6-d5*d4;if(va<=0&&d4-d3>=0&&d5-d6>=0){_vbc.subVectors(c,b);w=(d4-d3)/(d4-d3+(d5-d6));return target.copy(b).addScaledVector(_vbc,w)}const denom=1/(va+vb+vc);v=vb*denom;w=vc*denom;return target.copy(a).addScaledVector(_vab,v).addScaledVector(_vac,w)}equals(triangle){return triangle.a.equals(this.a)&&triangle.b.equals(this.b)&&triangle.c.equals(this.c)}}let materialId=0;class Material extends EventDispatcher$1{constructor(){super();Object.defineProperty(this,"id",{value:materialId++});this.uuid=generateUUID();this.name="";this.type="Material";this.fog=true;this.blending=NormalBlending;this.side=FrontSide;this.vertexColors=false;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.stencilWriteMask=255;this.stencilFunc=AlwaysStencilFunc;this.stencilRef=0;this.stencilFuncMask=255;this.stencilFail=KeepStencilOp;this.stencilZFail=KeepStencilOp;this.stencilZPass=KeepStencilOp;this.stencilWrite=false;this.clippingPlanes=null;this.clipIntersection=false;this.clipShadows=false;this.shadowSide=null;this.colorWrite=true;this.precision=null;this.polygonOffset=false;this.polygonOffsetFactor=0;this.polygonOffsetUnits=0;this.dithering=false;this.alphaToCoverage=false;this.premultipliedAlpha=false;this.visible=true;this.toneMapped=true;this.userData={};this.version=0;this._alphaTest=0}get alphaTest(){return this._alphaTest}set alphaTest(value){if(this._alphaTest>0!==value>0){this.version++}this._alphaTest=value}onBuild(){}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(values){if(values===undefined)return;for(const key in values){const newValue=values[key];if(newValue===undefined){console.warn("THREE.Material: '"+key+"' parameter is undefined.");continue}if(key==="shading"){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.");this.flatShading=newValue===FlatShading?true:false;continue}const currentValue=this[key];if(currentValue===undefined){console.warn("THREE."+this.type+": '"+key+"' is not a property of this material.");continue}if(currentValue&¤tValue.isColor){currentValue.set(newValue)}else if(currentValue&¤tValue.isVector3&&(newValue&&newValue.isVector3)){currentValue.copy(newValue)}else{this[key]=newValue}}}toJSON(meta){const isRootObject=meta===undefined||typeof meta==="string";if(isRootObject){meta={textures:{},images:{}}}const data={metadata:{version:4.5,type:"Material",generator:"Material.toJSON"}};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.sheen!==undefined)data.sheen=this.sheen;if(this.sheenColor&&this.sheenColor.isColor)data.sheenColor=this.sheenColor.getHex();if(this.sheenRoughness!==undefined)data.sheenRoughness=this.sheenRoughness;if(this.emissive&&this.emissive.isColor)data.emissive=this.emissive.getHex();if(this.emissiveIntensity&&this.emissiveIntensity!==1)data.emissiveIntensity=this.emissiveIntensity;if(this.specular&&this.specular.isColor)data.specular=this.specular.getHex();if(this.specularIntensity!==undefined)data.specularIntensity=this.specularIntensity;if(this.specularColor&&this.specularColor.isColor)data.specularColor=this.specularColor.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.clearcoatMap&&this.clearcoatMap.isTexture){data.clearcoatMap=this.clearcoatMap.toJSON(meta).uuid}if(this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture){data.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(meta).uuid}if(this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture){data.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(meta).uuid;data.clearcoatNormalScale=this.clearcoatNormalScale.toArray()}if(this.map&&this.map.isTexture)data.map=this.map.toJSON(meta).uuid;if(this.matcap&&this.matcap.isTexture)data.matcap=this.matcap.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;data.lightMapIntensity=this.lightMapIntensity}if(this.aoMap&&this.aoMap.isTexture){data.aoMap=this.aoMap.toJSON(meta).uuid;data.aoMapIntensity=this.aoMapIntensity}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.normalMapType=this.normalMapType;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.specularIntensityMap&&this.specularIntensityMap.isTexture)data.specularIntensityMap=this.specularIntensityMap.toJSON(meta).uuid;if(this.specularColorMap&&this.specularColorMap.isTexture)data.specularColorMap=this.specularColorMap.toJSON(meta).uuid;if(this.envMap&&this.envMap.isTexture){data.envMap=this.envMap.toJSON(meta).uuid;if(this.combine!==undefined)data.combine=this.combine}if(this.envMapIntensity!==undefined)data.envMapIntensity=this.envMapIntensity;if(this.reflectivity!==undefined)data.reflectivity=this.reflectivity;if(this.refractionRatio!==undefined)data.refractionRatio=this.refractionRatio;if(this.gradientMap&&this.gradientMap.isTexture){data.gradientMap=this.gradientMap.toJSON(meta).uuid}if(this.transmission!==undefined)data.transmission=this.transmission;if(this.transmissionMap&&this.transmissionMap.isTexture)data.transmissionMap=this.transmissionMap.toJSON(meta).uuid;if(this.thickness!==undefined)data.thickness=this.thickness;if(this.thicknessMap&&this.thicknessMap.isTexture)data.thicknessMap=this.thicknessMap.toJSON(meta).uuid;if(this.attenuationDistance!==undefined)data.attenuationDistance=this.attenuationDistance;if(this.attenuationColor!==undefined)data.attenuationColor=this.attenuationColor.getHex();if(this.size!==undefined)data.size=this.size;if(this.shadowSide!==null)data.shadowSide=this.shadowSide;if(this.sizeAttenuation!==undefined)data.sizeAttenuation=this.sizeAttenuation;if(this.blending!==NormalBlending)data.blending=this.blending;if(this.side!==FrontSide)data.side=this.side;if(this.vertexColors)data.vertexColors=true;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;data.colorWrite=this.colorWrite;data.stencilWrite=this.stencilWrite;data.stencilWriteMask=this.stencilWriteMask;data.stencilFunc=this.stencilFunc;data.stencilRef=this.stencilRef;data.stencilFuncMask=this.stencilFuncMask;data.stencilFail=this.stencilFail;data.stencilZFail=this.stencilZFail;data.stencilZPass=this.stencilZPass;if(this.rotation!==undefined&&this.rotation!==0)data.rotation=this.rotation;if(this.polygonOffset===true)data.polygonOffset=true;if(this.polygonOffsetFactor!==0)data.polygonOffsetFactor=this.polygonOffsetFactor;if(this.polygonOffsetUnits!==0)data.polygonOffsetUnits=this.polygonOffsetUnits;if(this.linewidth!==undefined&&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.alphaToCoverage===true)data.alphaToCoverage=this.alphaToCoverage;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.flatShading===true)data.flatShading=this.flatShading;if(this.visible===false)data.visible=false;if(this.toneMapped===false)data.toneMapped=false;if(JSON.stringify(this.userData)!=="{}")data.userData=this.userData;function extractFromCache(cache){const values=[];for(const key in cache){const data=cache[key];delete data.metadata;values.push(data)}return values}if(isRootObject){const textures=extractFromCache(meta.textures);const images=extractFromCache(meta.images);if(textures.length>0)data.textures=textures;if(images.length>0)data.images=images}return data}clone(){return(new this.constructor).copy(this)}copy(source){this.name=source.name;this.fog=source.fog;this.blending=source.blending;this.side=source.side;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.stencilWriteMask=source.stencilWriteMask;this.stencilFunc=source.stencilFunc;this.stencilRef=source.stencilRef;this.stencilFuncMask=source.stencilFuncMask;this.stencilFail=source.stencilFail;this.stencilZFail=source.stencilZFail;this.stencilZPass=source.stencilZPass;this.stencilWrite=source.stencilWrite;const srcPlanes=source.clippingPlanes;let dstPlanes=null;if(srcPlanes!==null){const n=srcPlanes.length;dstPlanes=new Array(n);for(let i=0;i!==n;++i){dstPlanes[i]=srcPlanes[i].clone()}}this.clippingPlanes=dstPlanes;this.clipIntersection=source.clipIntersection;this.clipShadows=source.clipShadows;this.shadowSide=source.shadowSide;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.alphaToCoverage=source.alphaToCoverage;this.premultipliedAlpha=source.premultipliedAlpha;this.visible=source.visible;this.toneMapped=source.toneMapped;this.userData=JSON.parse(JSON.stringify(source.userData));return this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(value){if(value===true)this.version++}}Material.prototype.isMaterial=true;Material.fromType=function(){return null};class MeshBasicMaterial extends Material{constructor(parameters){super();this.type="MeshBasicMaterial";this.color=new Color(16777215);this.map=null;this.lightMap=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.specularMap=null;this.alphaMap=null;this.envMap=null;this.combine=MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.wireframe=false;this.wireframeLinewidth=1;this.wireframeLinecap="round";this.wireframeLinejoin="round";this.setValues(parameters)}copy(source){super.copy(source);this.color.copy(source.color);this.map=source.map;this.lightMap=source.lightMap;this.lightMapIntensity=source.lightMapIntensity;this.aoMap=source.aoMap;this.aoMapIntensity=source.aoMapIntensity;this.specularMap=source.specularMap;this.alphaMap=source.alphaMap;this.envMap=source.envMap;this.combine=source.combine;this.reflectivity=source.reflectivity;this.refractionRatio=source.refractionRatio;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;this.wireframeLinecap=source.wireframeLinecap;this.wireframeLinejoin=source.wireframeLinejoin;return this}}MeshBasicMaterial.prototype.isMeshBasicMaterial=true;const _vector$9=new Vector3;const _vector2$1=new Vector2;class BufferAttribute{constructor(array,itemSize,normalized){if(Array.isArray(array)){throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.")}this.name="";this.array=array;this.itemSize=itemSize;this.count=array!==undefined?array.length/itemSize:0;this.normalized=normalized===true;this.usage=StaticDrawUsage;this.updateRange={offset:0,count:-1};this.version=0}onUploadCallback(){}set needsUpdate(value){if(value===true)this.version++}setUsage(value){this.usage=value;return this}copy(source){this.name=source.name;this.array=new source.array.constructor(source.array);this.itemSize=source.itemSize;this.count=source.count;this.normalized=source.normalized;this.usage=source.usage;return this}copyAt(index1,attribute,index2){index1*=this.itemSize;index2*=attribute.itemSize;for(let i=0,l=this.itemSize;i0)data.userData=this.userData;if(this.parameters!==undefined){const parameters=this.parameters;for(const key in parameters){if(parameters[key]!==undefined)data[key]=parameters[key]}return data}data.data={attributes:{}};const index=this.index;if(index!==null){data.data.index={type:index.array.constructor.name,array:Array.prototype.slice.call(index.array)}}const attributes=this.attributes;for(const key in attributes){const attribute=attributes[key];data.data.attributes[key]=attribute.toJSON(data.data)}const morphAttributes={};let hasMorphAttributes=false;for(const key in this.morphAttributes){const attributeArray=this.morphAttributes[key];const array=[];for(let i=0,il=attributeArray.length;i0){morphAttributes[key]=array;hasMorphAttributes=true}}if(hasMorphAttributes){data.data.morphAttributes=morphAttributes;data.data.morphTargetsRelative=this.morphTargetsRelative}const groups=this.groups;if(groups.length>0){data.data.groups=JSON.parse(JSON.stringify(groups))}const boundingSphere=this.boundingSphere;if(boundingSphere!==null){data.data.boundingSphere={center:boundingSphere.center.toArray(),radius:boundingSphere.radius}}return data}clone(){return(new this.constructor).copy(this)}copy(source){this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingBox=null;this.boundingSphere=null;const data={};this.name=source.name;const index=source.index;if(index!==null){this.setIndex(index.clone(data))}const attributes=source.attributes;for(const name in attributes){const attribute=attributes[name];this.setAttribute(name,attribute.clone(data))}const morphAttributes=source.morphAttributes;for(const name in morphAttributes){const array=[];const morphAttribute=morphAttributes[name];for(let i=0,l=morphAttribute.length;i0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}}}raycast(raycaster,intersects){const geometry=this.geometry;const material=this.material;const matrixWorld=this.matrixWorld;if(material===undefined)return;if(geometry.boundingSphere===null)geometry.computeBoundingSphere();_sphere$3.copy(geometry.boundingSphere);_sphere$3.applyMatrix4(matrixWorld);if(raycaster.ray.intersectsSphere(_sphere$3)===false)return;_inverseMatrix$2.copy(matrixWorld).invert();_ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2);if(geometry.boundingBox!==null){if(_ray$2.intersectsBox(geometry.boundingBox)===false)return}let intersection;if(geometry.isBufferGeometry){const index=geometry.index;const position=geometry.attributes.position;const morphPosition=geometry.morphAttributes.position;const morphTargetsRelative=geometry.morphTargetsRelative;const uv=geometry.attributes.uv;const uv2=geometry.attributes.uv2;const groups=geometry.groups;const drawRange=geometry.drawRange;if(index!==null){if(Array.isArray(material)){for(let i=0,il=groups.length;iraycaster.far)return null;return{distance:distance,point:_intersectionPointWorld.clone(),object:object}}function checkBufferGeometryIntersection(object,material,raycaster,ray,position,morphPosition,morphTargetsRelative,uv,uv2,a,b,c){_vA$1.fromBufferAttribute(position,a);_vB$1.fromBufferAttribute(position,b);_vC$1.fromBufferAttribute(position,c);const morphInfluences=object.morphTargetInfluences;if(morphPosition&&morphInfluences){_morphA.set(0,0,0);_morphB.set(0,0,0);_morphC.set(0,0,0);for(let i=0,il=morphPosition.length;i0?1:-1;normals.push(vector.x,vector.y,vector.z);uvs.push(ix/gridX);uvs.push(1-iy/gridY);vertexCounter+=1}}for(let iy=0;iy0)data.defines=this.defines;data.vertexShader=this.vertexShader;data.fragmentShader=this.fragmentShader;const extensions={};for(const key in this.extensions){if(this.extensions[key]===true)extensions[key]=true}if(Object.keys(extensions).length>0)data.extensions=extensions;return data}}ShaderMaterial.prototype.isShaderMaterial=true;class Camera extends Object3D{constructor(){super();this.type="Camera";this.matrixWorldInverse=new Matrix4;this.projectionMatrix=new Matrix4;this.projectionMatrixInverse=new Matrix4}copy(source,recursive){super.copy(source,recursive);this.matrixWorldInverse.copy(source.matrixWorldInverse);this.projectionMatrix.copy(source.projectionMatrix);this.projectionMatrixInverse.copy(source.projectionMatrixInverse);return this}getWorldDirection(target){this.updateWorldMatrix(true,false);const e=this.matrixWorld.elements;return target.set(-e[8],-e[9],-e[10]).normalize()}updateMatrixWorld(force){super.updateMatrixWorld(force);this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(updateParents,updateChildren){super.updateWorldMatrix(updateParents,updateChildren);this.matrixWorldInverse.copy(this.matrixWorld).invert()}clone(){return(new this.constructor).copy(this)}}Camera.prototype.isCamera=true;class PerspectiveCamera extends Camera{constructor(fov=50,aspect=1,near=.1,far=2e3){super();this.type="PerspectiveCamera";this.fov=fov;this.zoom=1;this.near=near;this.far=far;this.focus=10;this.aspect=aspect;this.view=null;this.filmGauge=35;this.filmOffset=0;this.updateProjectionMatrix()}copy(source,recursive){super.copy(source,recursive);this.fov=source.fov;this.zoom=source.zoom;this.near=source.near;this.far=source.far;this.focus=source.focus;this.aspect=source.aspect;this.view=source.view===null?null:Object.assign({},source.view);this.filmGauge=source.filmGauge;this.filmOffset=source.filmOffset;return this}setFocalLength(focalLength){const vExtentSlope=.5*this.getFilmHeight()/focalLength;this.fov=RAD2DEG*2*Math.atan(vExtentSlope);this.updateProjectionMatrix()}getFocalLength(){const vExtentSlope=Math.tan(DEG2RAD*.5*this.fov);return.5*this.getFilmHeight()/vExtentSlope}getEffectiveFOV(){return RAD2DEG*2*Math.atan(Math.tan(DEG2RAD*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}setViewOffset(fullWidth,fullHeight,x,y,width,height){this.aspect=fullWidth/fullHeight;if(this.view===null){this.view={enabled:true,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}}this.view.enabled=true;this.view.fullWidth=fullWidth;this.view.fullHeight=fullHeight;this.view.offsetX=x;this.view.offsetY=y;this.view.width=width;this.view.height=height;this.updateProjectionMatrix()}clearViewOffset(){if(this.view!==null){this.view.enabled=false}this.updateProjectionMatrix()}updateProjectionMatrix(){const near=this.near;let top=near*Math.tan(DEG2RAD*.5*this.fov)/this.zoom;let height=2*top;let width=this.aspect*height;let left=-.5*width;const view=this.view;if(this.view!==null&&this.view.enabled){const fullWidth=view.fullWidth,fullHeight=view.fullHeight;left+=view.offsetX*width/fullWidth;top-=view.offsetY*height/fullHeight;width*=view.width/fullWidth;height*=view.height/fullHeight}const skew=this.filmOffset;if(skew!==0)left+=near*skew/this.getFilmWidth();this.projectionMatrix.makePerspective(left,left+width,top,top-height,near,this.far);this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(meta){const data=super.toJSON(meta);data.object.fov=this.fov;data.object.zoom=this.zoom;data.object.near=this.near;data.object.far=this.far;data.object.focus=this.focus;data.object.aspect=this.aspect;if(this.view!==null)data.object.view=Object.assign({},this.view);data.object.filmGauge=this.filmGauge;data.object.filmOffset=this.filmOffset;return data}}PerspectiveCamera.prototype.isPerspectiveCamera=true;const fov=90,aspect=1;class CubeCamera extends Object3D{constructor(near,far,renderTarget){super();this.type="CubeCamera";if(renderTarget.isWebGLCubeRenderTarget!==true){console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.");return}this.renderTarget=renderTarget;const cameraPX=new PerspectiveCamera(fov,aspect,near,far);cameraPX.layers=this.layers;cameraPX.up.set(0,-1,0);cameraPX.lookAt(new Vector3(1,0,0));this.add(cameraPX);const cameraNX=new PerspectiveCamera(fov,aspect,near,far);cameraNX.layers=this.layers;cameraNX.up.set(0,-1,0);cameraNX.lookAt(new Vector3(-1,0,0));this.add(cameraNX);const cameraPY=new PerspectiveCamera(fov,aspect,near,far);cameraPY.layers=this.layers;cameraPY.up.set(0,0,1);cameraPY.lookAt(new Vector3(0,1,0));this.add(cameraPY);const cameraNY=new PerspectiveCamera(fov,aspect,near,far);cameraNY.layers=this.layers;cameraNY.up.set(0,0,-1);cameraNY.lookAt(new Vector3(0,-1,0));this.add(cameraNY);const cameraPZ=new PerspectiveCamera(fov,aspect,near,far);cameraPZ.layers=this.layers;cameraPZ.up.set(0,-1,0);cameraPZ.lookAt(new Vector3(0,0,1));this.add(cameraPZ);const cameraNZ=new PerspectiveCamera(fov,aspect,near,far);cameraNZ.layers=this.layers;cameraNZ.up.set(0,-1,0);cameraNZ.lookAt(new Vector3(0,0,-1));this.add(cameraNZ)}update(renderer,scene){if(this.parent===null)this.updateMatrixWorld();const renderTarget=this.renderTarget;const[cameraPX,cameraNX,cameraPY,cameraNY,cameraPZ,cameraNZ]=this.children;const currentRenderTarget=renderer.getRenderTarget();const currentOutputEncoding=renderer.outputEncoding;const currentToneMapping=renderer.toneMapping;const currentXrEnabled=renderer.xr.enabled;renderer.outputEncoding=LinearEncoding;renderer.toneMapping=NoToneMapping;renderer.xr.enabled=false;const generateMipmaps=renderTarget.texture.generateMipmaps;renderTarget.texture.generateMipmaps=false;renderer.setRenderTarget(renderTarget,0);renderer.render(scene,cameraPX);renderer.setRenderTarget(renderTarget,1);renderer.render(scene,cameraNX);renderer.setRenderTarget(renderTarget,2);renderer.render(scene,cameraPY);renderer.setRenderTarget(renderTarget,3);renderer.render(scene,cameraNY);renderer.setRenderTarget(renderTarget,4);renderer.render(scene,cameraPZ);renderTarget.texture.generateMipmaps=generateMipmaps;renderer.setRenderTarget(renderTarget,5);renderer.render(scene,cameraNZ);renderer.setRenderTarget(currentRenderTarget);renderer.outputEncoding=currentOutputEncoding;renderer.toneMapping=currentToneMapping;renderer.xr.enabled=currentXrEnabled;renderTarget.texture.needsPMREMUpdate=true}}class CubeTexture extends Texture{constructor(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding){images=images!==undefined?images:[];mapping=mapping!==undefined?mapping:CubeReflectionMapping;super(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this.flipY=false}get images(){return this.image}set images(value){this.image=value}}CubeTexture.prototype.isCubeTexture=true;class WebGLCubeRenderTarget extends WebGLRenderTarget{constructor(size,options={}){super(size,size,options);const image={width:size,height:size,depth:1};const images=[image,image,image,image,image,image];this.texture=new CubeTexture(images,options.mapping,options.wrapS,options.wrapT,options.magFilter,options.minFilter,options.format,options.type,options.anisotropy,options.encoding);this.texture.isRenderTargetTexture=true;this.texture.generateMipmaps=options.generateMipmaps!==undefined?options.generateMipmaps:false;this.texture.minFilter=options.minFilter!==undefined?options.minFilter:LinearFilter}fromEquirectangularTexture(renderer,texture){this.texture.type=texture.type;this.texture.encoding=texture.encoding;this.texture.generateMipmaps=texture.generateMipmaps;this.texture.minFilter=texture.minFilter;this.texture.magFilter=texture.magFilter;const shader={uniforms:{tEquirect:{value:null}},vertexShader:`\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t`,fragmentShader:`\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t`};const geometry=new BoxGeometry(5,5,5);const material=new ShaderMaterial({name:"CubemapFromEquirect",uniforms:cloneUniforms(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader,side:BackSide,blending:NoBlending});material.uniforms.tEquirect.value=texture;const mesh=new Mesh(geometry,material);const currentMinFilter=texture.minFilter;if(texture.minFilter===LinearMipmapLinearFilter)texture.minFilter=LinearFilter;const camera=new CubeCamera(1,10,this);camera.update(renderer,mesh);texture.minFilter=currentMinFilter;mesh.geometry.dispose();mesh.material.dispose();return this}clear(renderer,color,depth,stencil){const currentRenderTarget=renderer.getRenderTarget();for(let i=0;i<6;i++){renderer.setRenderTarget(this,i);renderer.clear(color,depth,stencil)}renderer.setRenderTarget(currentRenderTarget)}}WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget=true;const _vector1=new Vector3;const _vector2=new Vector3;const _normalMatrix=new Matrix3;class Plane{constructor(normal=new Vector3(1,0,0),constant=0){this.normal=normal;this.constant=constant}set(normal,constant){this.normal.copy(normal);this.constant=constant;return this}setComponents(x,y,z,w){this.normal.set(x,y,z);this.constant=w;return this}setFromNormalAndCoplanarPoint(normal,point){this.normal.copy(normal);this.constant=-point.dot(this.normal);return this}setFromCoplanarPoints(a,b,c){const normal=_vector1.subVectors(c,b).cross(_vector2.subVectors(a,b)).normalize();this.setFromNormalAndCoplanarPoint(normal,a);return this}copy(plane){this.normal.copy(plane.normal);this.constant=plane.constant;return this}normalize(){const inverseNormalLength=1/this.normal.length();this.normal.multiplyScalar(inverseNormalLength);this.constant*=inverseNormalLength;return this}negate(){this.constant*=-1;this.normal.negate();return this}distanceToPoint(point){return this.normal.dot(point)+this.constant}distanceToSphere(sphere){return this.distanceToPoint(sphere.center)-sphere.radius}projectPoint(point,target){return target.copy(this.normal).multiplyScalar(-this.distanceToPoint(point)).add(point)}intersectLine(line,target){const direction=line.delta(_vector1);const denominator=this.normal.dot(direction);if(denominator===0){if(this.distanceToPoint(line.start)===0){return target.copy(line.start)}return null}const t=-(line.start.dot(this.normal)+this.constant)/denominator;if(t<0||t>1){return null}return target.copy(direction).multiplyScalar(t).add(line.start)}intersectsLine(line){const startSign=this.distanceToPoint(line.start);const endSign=this.distanceToPoint(line.end);return startSign<0&&endSign>0||endSign<0&&startSign>0}intersectsBox(box){return box.intersectsPlane(this)}intersectsSphere(sphere){return sphere.intersectsPlane(this)}coplanarPoint(target){return target.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(matrix,optionalNormalMatrix){const normalMatrix=optionalNormalMatrix||_normalMatrix.getNormalMatrix(matrix);const referencePoint=this.coplanarPoint(_vector1).applyMatrix4(matrix);const normal=this.normal.applyMatrix3(normalMatrix).normalize();this.constant=-referencePoint.dot(normal);return this}translate(offset){this.constant-=offset.dot(this.normal);return this}equals(plane){return plane.normal.equals(this.normal)&&plane.constant===this.constant}clone(){return(new this.constructor).copy(this)}}Plane.prototype.isPlane=true;const _sphere$2=new Sphere;const _vector$7=new Vector3;class Frustum{constructor(p0=new Plane,p1=new Plane,p2=new Plane,p3=new Plane,p4=new Plane,p5=new Plane){this.planes=[p0,p1,p2,p3,p4,p5]}set(p0,p1,p2,p3,p4,p5){const planes=this.planes;planes[0].copy(p0);planes[1].copy(p1);planes[2].copy(p2);planes[3].copy(p3);planes[4].copy(p4);planes[5].copy(p5);return this}copy(frustum){const planes=this.planes;for(let i=0;i<6;i++){planes[i].copy(frustum.planes[i])}return this}setFromProjectionMatrix(m){const planes=this.planes;const me=m.elements;const me0=me[0],me1=me[1],me2=me[2],me3=me[3];const me4=me[4],me5=me[5],me6=me[6],me7=me[7];const me8=me[8],me9=me[9],me10=me[10],me11=me[11];const me12=me[12],me13=me[13],me14=me[14],me15=me[15];planes[0].setComponents(me3-me0,me7-me4,me11-me8,me15-me12).normalize();planes[1].setComponents(me3+me0,me7+me4,me11+me8,me15+me12).normalize();planes[2].setComponents(me3+me1,me7+me5,me11+me9,me15+me13).normalize();planes[3].setComponents(me3-me1,me7-me5,me11-me9,me15-me13).normalize();planes[4].setComponents(me3-me2,me7-me6,me11-me10,me15-me14).normalize();planes[5].setComponents(me3+me2,me7+me6,me11+me10,me15+me14).normalize();return this}intersectsObject(object){const geometry=object.geometry;if(geometry.boundingSphere===null)geometry.computeBoundingSphere();_sphere$2.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);return this.intersectsSphere(_sphere$2)}intersectsSprite(sprite){_sphere$2.center.set(0,0,0);_sphere$2.radius=.7071067811865476;_sphere$2.applyMatrix4(sprite.matrixWorld);return this.intersectsSphere(_sphere$2)}intersectsSphere(sphere){const planes=this.planes;const center=sphere.center;const negRadius=-sphere.radius;for(let i=0;i<6;i++){const distance=planes[i].distanceToPoint(center);if(distance0?box.max.x:box.min.x;_vector$7.y=plane.normal.y>0?box.max.y:box.min.y;_vector$7.z=plane.normal.z>0?box.max.z:box.min.z;if(plane.distanceToPoint(_vector$7)<0){return false}}return true}containsPoint(point){const planes=this.planes;for(let i=0;i<6;i++){if(planes[i].distanceToPoint(point)<0){return false}}return true}clone(){return(new this.constructor).copy(this)}}function WebGLAnimation(){let context=null;let isAnimating=false;let animationLoop=null;let requestId=null;function onAnimationFrame(time,frame){animationLoop(time,frame);requestId=context.requestAnimationFrame(onAnimationFrame)}return{start:function(){if(isAnimating===true)return;if(animationLoop===null)return;requestId=context.requestAnimationFrame(onAnimationFrame);isAnimating=true},stop:function(){context.cancelAnimationFrame(requestId);isAnimating=false},setAnimationLoop:function(callback){animationLoop=callback},setContext:function(value){context=value}}}function WebGLAttributes(gl,capabilities){const isWebGL2=capabilities.isWebGL2;const buffers=new WeakMap;function createBuffer(attribute,bufferType){const array=attribute.array;const usage=attribute.usage;const buffer=gl.createBuffer();gl.bindBuffer(bufferType,buffer);gl.bufferData(bufferType,array,usage);attribute.onUploadCallback();let type;if(array instanceof Float32Array){type=5126}else if(array instanceof Uint16Array){if(attribute.isFloat16BufferAttribute){if(isWebGL2){type=5131}else{throw new Error("THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.")}}else{type=5123}}else if(array instanceof Int16Array){type=5122}else if(array instanceof Uint32Array){type=5125}else if(array instanceof Int32Array){type=5124}else if(array instanceof Int8Array){type=5120}else if(array instanceof Uint8Array){type=5121}else if(array instanceof Uint8ClampedArray){type=5121}else{throw new Error("THREE.WebGLAttributes: Unsupported buffer data format: "+array)}return{buffer:buffer,type:type,bytesPerElement:array.BYTES_PER_ELEMENT,version:attribute.version}}function updateBuffer(buffer,attribute,bufferType){const array=attribute.array;const updateRange=attribute.updateRange;gl.bindBuffer(bufferType,buffer);if(updateRange.count===-1){gl.bufferSubData(bufferType,0,array)}else{if(isWebGL2){gl.bufferSubData(bufferType,updateRange.offset*array.BYTES_PER_ELEMENT,array,updateRange.offset,updateRange.count)}else{gl.bufferSubData(bufferType,updateRange.offset*array.BYTES_PER_ELEMENT,array.subarray(updateRange.offset,updateRange.offset+updateRange.count))}updateRange.count=-1}}function get(attribute){if(attribute.isInterleavedBufferAttribute)attribute=attribute.data;return buffers.get(attribute)}function remove(attribute){if(attribute.isInterleavedBufferAttribute)attribute=attribute.data;const data=buffers.get(attribute);if(data){gl.deleteBuffer(data.buffer);buffers.delete(attribute)}}function update(attribute,bufferType){if(attribute.isGLBufferAttribute){const cached=buffers.get(attribute);if(!cached||cached.version=0){let geometryAttribute=geometryAttributes[name];if(geometryAttribute===undefined){if(name==="instanceMatrix"&&object.instanceMatrix)geometryAttribute=object.instanceMatrix;if(name==="instanceColor"&&object.instanceColor)geometryAttribute=object.instanceColor}if(geometryAttribute!==undefined){const normalized=geometryAttribute.normalized;const size=geometryAttribute.itemSize;const attribute=attributes.get(geometryAttribute);if(attribute===undefined)continue;const buffer=attribute.buffer;const type=attribute.type;const bytesPerElement=attribute.bytesPerElement;if(geometryAttribute.isInterleavedBufferAttribute){const data=geometryAttribute.data;const stride=data.stride;const offset=geometryAttribute.offset;if(data.isInstancedInterleavedBuffer){for(let i=0;i0&&gl.getShaderPrecisionFormat(35632,36338).precision>0){return"highp"}precision="mediump"}if(precision==="mediump"){if(gl.getShaderPrecisionFormat(35633,36337).precision>0&&gl.getShaderPrecisionFormat(35632,36337).precision>0){return"mediump"}}return"lowp"}const isWebGL2=typeof WebGL2RenderingContext!=="undefined"&&gl instanceof WebGL2RenderingContext||typeof WebGL2ComputeRenderingContext!=="undefined"&&gl instanceof WebGL2ComputeRenderingContext;let precision=parameters.precision!==undefined?parameters.precision:"highp";const maxPrecision=getMaxPrecision(precision);if(maxPrecision!==precision){console.warn("THREE.WebGLRenderer:",precision,"not supported, using",maxPrecision,"instead.");precision=maxPrecision}const drawBuffers=isWebGL2||extensions.has("WEBGL_draw_buffers");const logarithmicDepthBuffer=parameters.logarithmicDepthBuffer===true;const maxTextures=gl.getParameter(34930);const maxVertexTextures=gl.getParameter(35660);const maxTextureSize=gl.getParameter(3379);const maxCubemapSize=gl.getParameter(34076);const maxAttributes=gl.getParameter(34921);const maxVertexUniforms=gl.getParameter(36347);const maxVaryings=gl.getParameter(36348);const maxFragmentUniforms=gl.getParameter(36349);const vertexTextures=maxVertexTextures>0;const floatFragmentTextures=isWebGL2||extensions.has("OES_texture_float");const floatVertexTextures=vertexTextures&&floatFragmentTextures;const maxSamples=isWebGL2?gl.getParameter(36183):0;return{isWebGL2:isWebGL2,drawBuffers:drawBuffers,getMaxAnisotropy:getMaxAnisotropy,getMaxPrecision:getMaxPrecision,precision:precision,logarithmicDepthBuffer:logarithmicDepthBuffer,maxTextures:maxTextures,maxVertexTextures:maxVertexTextures,maxTextureSize:maxTextureSize,maxCubemapSize:maxCubemapSize,maxAttributes:maxAttributes,maxVertexUniforms:maxVertexUniforms,maxVaryings:maxVaryings,maxFragmentUniforms:maxFragmentUniforms,vertexTextures:vertexTextures,floatFragmentTextures:floatFragmentTextures,floatVertexTextures:floatVertexTextures,maxSamples:maxSamples}}function WebGLClipping(properties){const scope=this;let globalState=null,numGlobalPlanes=0,localClippingEnabled=false,renderingShadows=false;const plane=new Plane,viewNormalMatrix=new Matrix3,uniform={value:null,needsUpdate:false};this.uniform=uniform;this.numPlanes=0;this.numIntersection=0;this.init=function(planes,enableLocalClipping,camera){const enabled=planes.length!==0||enableLocalClipping||numGlobalPlanes!==0||localClippingEnabled;localClippingEnabled=enableLocalClipping;globalState=projectPlanes(planes,camera,0);numGlobalPlanes=planes.length;return enabled};this.beginShadows=function(){renderingShadows=true;projectPlanes(null)};this.endShadows=function(){renderingShadows=false;resetGlobalState()};this.setState=function(material,camera,useCache){const planes=material.clippingPlanes,clipIntersection=material.clipIntersection,clipShadows=material.clipShadows;const materialProperties=properties.get(material);if(!localClippingEnabled||planes===null||planes.length===0||renderingShadows&&!clipShadows){if(renderingShadows){projectPlanes(null)}else{resetGlobalState()}}else{const nGlobal=renderingShadows?0:numGlobalPlanes,lGlobal=nGlobal*4;let dstArray=materialProperties.clippingState||null;uniform.value=dstArray;dstArray=projectPlanes(planes,camera,lGlobal,useCache);for(let i=0;i!==lGlobal;++i){dstArray[i]=globalState[i]}materialProperties.clippingState=dstArray;this.numIntersection=clipIntersection?this.numPlanes:0;this.numPlanes+=nGlobal}};function resetGlobalState(){if(uniform.value!==globalState){uniform.value=globalState;uniform.needsUpdate=numGlobalPlanes>0}scope.numPlanes=numGlobalPlanes;scope.numIntersection=0}function projectPlanes(planes,camera,dstOffset,skipTransform){const nPlanes=planes!==null?planes.length:0;let dstArray=null;if(nPlanes!==0){dstArray=uniform.value;if(skipTransform!==true||dstArray===null){const flatSize=dstOffset+nPlanes*4,viewMatrix=camera.matrixWorldInverse;viewNormalMatrix.getNormalMatrix(viewMatrix);if(dstArray===null||dstArray.length0){const renderTarget=new WebGLCubeRenderTarget(image.height/2);renderTarget.fromEquirectangularTexture(renderer,texture);cubemaps.set(texture,renderTarget);texture.addEventListener("dispose",onTextureDispose);return mapTextureMapping(renderTarget.texture,texture.mapping)}else{return null}}}}return texture}function onTextureDispose(event){const texture=event.target;texture.removeEventListener("dispose",onTextureDispose);const cubemap=cubemaps.get(texture);if(cubemap!==undefined){cubemaps.delete(texture);cubemap.dispose()}}function dispose(){cubemaps=new WeakMap}return{get:get,dispose:dispose}}class OrthographicCamera extends Camera{constructor(left=-1,right=1,top=1,bottom=-1,near=.1,far=2e3){super();this.type="OrthographicCamera";this.zoom=1;this.view=null;this.left=left;this.right=right;this.top=top;this.bottom=bottom;this.near=near;this.far=far;this.updateProjectionMatrix()}copy(source,recursive){super.copy(source,recursive);this.left=source.left;this.right=source.right;this.top=source.top;this.bottom=source.bottom;this.near=source.near;this.far=source.far;this.zoom=source.zoom;this.view=source.view===null?null:Object.assign({},source.view);return this}setViewOffset(fullWidth,fullHeight,x,y,width,height){if(this.view===null){this.view={enabled:true,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}}this.view.enabled=true;this.view.fullWidth=fullWidth;this.view.fullHeight=fullHeight;this.view.offsetX=x;this.view.offsetY=y;this.view.width=width;this.view.height=height;this.updateProjectionMatrix()}clearViewOffset(){if(this.view!==null){this.view.enabled=false}this.updateProjectionMatrix()}updateProjectionMatrix(){const dx=(this.right-this.left)/(2*this.zoom);const dy=(this.top-this.bottom)/(2*this.zoom);const cx=(this.right+this.left)/2;const cy=(this.top+this.bottom)/2;let left=cx-dx;let right=cx+dx;let top=cy+dy;let bottom=cy-dy;if(this.view!==null&&this.view.enabled){const scaleW=(this.right-this.left)/this.view.fullWidth/this.zoom;const scaleH=(this.top-this.bottom)/this.view.fullHeight/this.zoom;left+=scaleW*this.view.offsetX;right=left+scaleW*this.view.width;top-=scaleH*this.view.offsetY;bottom=top-scaleH*this.view.height}this.projectionMatrix.makeOrthographic(left,right,top,bottom,this.near,this.far);this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(meta){const data=super.toJSON(meta);data.object.zoom=this.zoom;data.object.left=this.left;data.object.right=this.right;data.object.top=this.top;data.object.bottom=this.bottom;data.object.near=this.near;data.object.far=this.far;if(this.view!==null)data.object.view=Object.assign({},this.view);return data}}OrthographicCamera.prototype.isOrthographicCamera=true;const LOD_MIN=4;const EXTRA_LOD_SIGMA=[.125,.215,.35,.446,.526,.582];const MAX_SAMPLES=20;const _flatCamera=new OrthographicCamera;const _clearColor=new Color;let _oldTarget=null;const PHI=(1+Math.sqrt(5))/2;const INV_PHI=1/PHI;const _axisDirections=[new Vector3(1,1,1),new Vector3(-1,1,1),new Vector3(1,1,-1),new Vector3(-1,1,-1),new Vector3(0,PHI,INV_PHI),new Vector3(0,PHI,-INV_PHI),new Vector3(INV_PHI,0,PHI),new Vector3(-INV_PHI,0,PHI),new Vector3(PHI,INV_PHI,0),new Vector3(-PHI,INV_PHI,0)];class PMREMGenerator{constructor(renderer){this._renderer=renderer;this._pingPongRenderTarget=null;this._lodMax=0;this._cubeSize=0;this._lodPlanes=[];this._sizeLods=[];this._sigmas=[];this._blurMaterial=null;this._cubemapMaterial=null;this._equirectMaterial=null;this._compileMaterial(this._blurMaterial)}fromScene(scene,sigma=0,near=.1,far=100){_oldTarget=this._renderer.getRenderTarget();this._setSize(256);const cubeUVRenderTarget=this._allocateTargets();cubeUVRenderTarget.depthBuffer=true;this._sceneToCubeUV(scene,near,far,cubeUVRenderTarget);if(sigma>0){this._blur(cubeUVRenderTarget,0,0,sigma)}this._applyPMREM(cubeUVRenderTarget);this._cleanup(cubeUVRenderTarget);return cubeUVRenderTarget}fromEquirectangular(equirectangular,renderTarget=null){return this._fromTexture(equirectangular,renderTarget)}fromCubemap(cubemap,renderTarget=null){return this._fromTexture(cubemap,renderTarget)}compileCubemapShader(){if(this._cubemapMaterial===null){this._cubemapMaterial=_getCubemapMaterial();this._compileMaterial(this._cubemapMaterial)}}compileEquirectangularShader(){if(this._equirectMaterial===null){this._equirectMaterial=_getEquirectMaterial();this._compileMaterial(this._equirectMaterial)}}dispose(){this._dispose();if(this._cubemapMaterial!==null)this._cubemapMaterial.dispose();if(this._equirectMaterial!==null)this._equirectMaterial.dispose()}_setSize(cubeSize){this._lodMax=Math.floor(Math.log2(cubeSize));this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){if(this._blurMaterial!==null)this._blurMaterial.dispose();if(this._pingPongRenderTarget!==null)this._pingPongRenderTarget.dispose();for(let i=0;i2?size:0,size,size);renderer.setRenderTarget(cubeUVRenderTarget);if(useSolidColor){renderer.render(backgroundBox,cubeCamera)}renderer.render(scene,cubeCamera)}backgroundBox.geometry.dispose();backgroundBox.material.dispose();renderer.toneMapping=toneMapping;renderer.autoClear=originalAutoClear;scene.background=background}_textureToCubeUV(texture,cubeUVRenderTarget){const renderer=this._renderer;const isCubeTexture=texture.mapping===CubeReflectionMapping||texture.mapping===CubeRefractionMapping;if(isCubeTexture){if(this._cubemapMaterial===null){this._cubemapMaterial=_getCubemapMaterial()}this._cubemapMaterial.uniforms.flipEnvMap.value=texture.isRenderTargetTexture===false?-1:1}else{if(this._equirectMaterial===null){this._equirectMaterial=_getEquirectMaterial()}}const material=isCubeTexture?this._cubemapMaterial:this._equirectMaterial;const mesh=new Mesh(this._lodPlanes[0],material);const uniforms=material.uniforms;uniforms["envMap"].value=texture;const size=this._cubeSize;_setViewport(cubeUVRenderTarget,0,0,3*size,2*size);renderer.setRenderTarget(cubeUVRenderTarget);renderer.render(mesh,_flatCamera)}_applyPMREM(cubeUVRenderTarget){const renderer=this._renderer;const autoClear=renderer.autoClear;renderer.autoClear=false;for(let i=1;iMAX_SAMPLES){console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`)}const weights=[];let sum=0;for(let i=0;i_lodMax-LOD_MIN?lodOut-_lodMax+LOD_MIN:0);const y=4*(this._cubeSize-outputSize);_setViewport(targetOut,x,y,3*outputSize,2*outputSize);renderer.setRenderTarget(targetOut);renderer.render(blurMesh,_flatCamera)}}function _createPlanes(lodMax){const lodPlanes=[];const sizeLods=[];const sigmas=[];let lod=lodMax;const totalLods=lodMax-LOD_MIN+1+EXTRA_LOD_SIGMA.length;for(let i=0;ilodMax-LOD_MIN){sigma=EXTRA_LOD_SIGMA[i-lodMax+LOD_MIN-1]}else if(i===0){sigma=0}sigmas.push(sigma);const texelSize=1/(sizeLod-1);const min=-texelSize/2;const max=1+texelSize/2;const uv1=[min,min,max,min,max,max,min,min,max,max,min,max];const cubeFaces=6;const vertices=6;const positionSize=3;const uvSize=2;const faceIndexSize=1;const position=new Float32Array(positionSize*vertices*cubeFaces);const uv=new Float32Array(uvSize*vertices*cubeFaces);const faceIndex=new Float32Array(faceIndexSize*vertices*cubeFaces);for(let face=0;face2?0:-1;const coordinates=[x,y,0,x+2/3,y,0,x+2/3,y+1,0,x,y,0,x+2/3,y+1,0,x,y+1,0];position.set(coordinates,positionSize*vertices*face);uv.set(uv1,uvSize*vertices*face);const fill=[face,face,face,face,face,face];faceIndex.set(fill,faceIndexSize*vertices*face)}const planes=new BufferGeometry;planes.setAttribute("position",new BufferAttribute(position,positionSize));planes.setAttribute("uv",new BufferAttribute(uv,uvSize));planes.setAttribute("faceIndex",new BufferAttribute(faceIndex,faceIndexSize));lodPlanes.push(planes);if(lod>LOD_MIN){lod--}}return{lodPlanes:lodPlanes,sizeLods:sizeLods,sigmas:sigmas}}function _createRenderTarget(width,height,params){const cubeUVRenderTarget=new WebGLRenderTarget(width,height,params);cubeUVRenderTarget.texture.mapping=CubeUVReflectionMapping;cubeUVRenderTarget.texture.name="PMREM.cubeUv";cubeUVRenderTarget.scissorTest=true;return cubeUVRenderTarget}function _setViewport(target,x,y,width,height){target.viewport.set(x,y,width,height);target.scissor.set(x,y,width,height)}function _getBlurShader(lodMax,width,height){const weights=new Float32Array(MAX_SAMPLES);const poleAxis=new Vector3(0,1,0);const shaderMaterial=new ShaderMaterial({name:"SphericalGaussianBlur",defines:{n:MAX_SAMPLES,CUBEUV_TEXEL_WIDTH:1/width,CUBEUV_TEXEL_HEIGHT:1/height,CUBEUV_MAX_MIP:`${lodMax}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:weights},latitudinal:{value:false},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:poleAxis}},vertexShader:_getCommonVertexShader(),fragmentShader:`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t}\n\t\t`,blending:NoBlending,depthTest:false,depthWrite:false});return shaderMaterial}function _getEquirectMaterial(){return new ShaderMaterial({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:_getCommonVertexShader(),fragmentShader:`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tgl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 );\n\n\t\t\t}\n\t\t`,blending:NoBlending,depthTest:false,depthWrite:false})}function _getCubemapMaterial(){return new ShaderMaterial({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:_getCommonVertexShader(),fragmentShader:`\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tuniform float flipEnvMap;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) );\n\n\t\t\t}\n\t\t`,blending:NoBlending,depthTest:false,depthWrite:false})}function _getCommonVertexShader(){return`\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t`}function WebGLCubeUVMaps(renderer){let cubeUVmaps=new WeakMap;let pmremGenerator=null;function get(texture){if(texture&&texture.isTexture){const mapping=texture.mapping;const isEquirectMap=mapping===EquirectangularReflectionMapping||mapping===EquirectangularRefractionMapping;const isCubeMap=mapping===CubeReflectionMapping||mapping===CubeRefractionMapping;if(isEquirectMap||isCubeMap){if(texture.isRenderTargetTexture&&texture.needsPMREMUpdate===true){texture.needsPMREMUpdate=false;let renderTarget=cubeUVmaps.get(texture);if(pmremGenerator===null)pmremGenerator=new PMREMGenerator(renderer);renderTarget=isEquirectMap?pmremGenerator.fromEquirectangular(texture,renderTarget):pmremGenerator.fromCubemap(texture,renderTarget);cubeUVmaps.set(texture,renderTarget);return renderTarget.texture}else{if(cubeUVmaps.has(texture)){return cubeUVmaps.get(texture).texture}else{const image=texture.image;if(isEquirectMap&&image&&image.height>0||isCubeMap&&image&&isCubeTextureComplete(image)){if(pmremGenerator===null)pmremGenerator=new PMREMGenerator(renderer);const renderTarget=isEquirectMap?pmremGenerator.fromEquirectangular(texture):pmremGenerator.fromCubemap(texture);cubeUVmaps.set(texture,renderTarget);texture.addEventListener("dispose",onTextureDispose);return renderTarget.texture}else{return null}}}}}return texture}function isCubeTextureComplete(image){let count=0;const length=6;for(let i=0;icapabilities.maxTextureSize){height=Math.ceil(width/capabilities.maxTextureSize);width=capabilities.maxTextureSize}const buffer=new Float32Array(width*height*4*morphTargetsCount);const texture=new DataArrayTexture(buffer,width,height,morphTargetsCount);texture.type=FloatType;texture.needsUpdate=true;const vertexDataStride=vertexDataCount*4;for(let i=0;i0)return array;const n=nBlocks*blockSize;let r=arrayCacheF32[n];if(r===undefined){r=new Float32Array(n);arrayCacheF32[n]=r}if(nBlocks!==0){firstElem.toArray(r,0);for(let i=1,offset=0;i!==nBlocks;++i){offset+=blockSize;array[i].toArray(r,offset)}}return r}function arraysEqual(a,b){if(a.length!==b.length)return false;for(let i=0,l=a.length;i/gm;function resolveIncludes(string){return string.replace(includePattern,includeReplacer)}function includeReplacer(match,include){const string=ShaderChunk[include];if(string===undefined){throw new Error("Can not resolve #include <"+include+">")}return resolveIncludes(string)}const deprecatedUnrollLoopPattern=/#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;const unrollLoopPattern=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function unrollLoops(string){return string.replace(unrollLoopPattern,loopReplacer).replace(deprecatedUnrollLoopPattern,deprecatedLoopReplacer)}function deprecatedLoopReplacer(match,start,end,snippet){console.warn("WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.");return loopReplacer(match,start,end,snippet)}function loopReplacer(match,start,end,snippet){let string="";for(let i=parseInt(start);i0){prefixVertex+="\n"}prefixFragment=[customExtensions,customDefines].filter(filterEmptyLine).join("\n");if(prefixFragment.length>0){prefixFragment+="\n"}}else{prefixVertex=[generatePrecision(parameters),"#define SHADER_NAME "+parameters.shaderName,customDefines,parameters.instancing?"#define USE_INSTANCING":"",parameters.instancingColor?"#define USE_INSTANCING_COLOR":"",parameters.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define MAX_BONES "+parameters.maxBones,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp2?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.normalMap&¶meters.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",parameters.normalMap&¶meters.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",parameters.clearcoatMap?"#define USE_CLEARCOATMAP":"",parameters.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",parameters.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",parameters.displacementMap&¶meters.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.specularIntensityMap?"#define USE_SPECULARINTENSITYMAP":"",parameters.specularColorMap?"#define USE_SPECULARCOLORMAP":"",parameters.roughnessMap?"#define USE_ROUGHNESSMAP":"",parameters.metalnessMap?"#define USE_METALNESSMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.transmission?"#define USE_TRANSMISSION":"",parameters.transmissionMap?"#define USE_TRANSMISSIONMAP":"",parameters.thicknessMap?"#define USE_THICKNESSMAP":"",parameters.sheenColorMap?"#define USE_SHEENCOLORMAP":"",parameters.sheenRoughnessMap?"#define USE_SHEENROUGHNESSMAP":"",parameters.vertexTangents?"#define USE_TANGENT":"",parameters.vertexColors?"#define USE_COLOR":"",parameters.vertexAlphas?"#define USE_COLOR_ALPHA":"",parameters.vertexUvs?"#define USE_UV":"",parameters.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.skinning?"#define USE_SKINNING":"",parameters.useVertexTexture?"#define BONE_TEXTURE":"",parameters.morphTargets?"#define USE_MORPHTARGETS":"",parameters.morphNormals&¶meters.flatShading===false?"#define USE_MORPHNORMALS":"",parameters.morphColors&¶meters.isWebGL2?"#define USE_MORPHCOLORS":"",parameters.morphTargetsCount>0&¶meters.isWebGL2?"#define MORPHTARGETS_TEXTURE":"",parameters.morphTargetsCount>0&¶meters.isWebGL2?"#define MORPHTARGETS_TEXTURE_STRIDE "+parameters.morphTextureStride:"",parameters.morphTargetsCount>0&¶meters.isWebGL2?"#define MORPHTARGETS_COUNT "+parameters.morphTargetsCount:"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.sizeAttenuation?"#define USE_SIZEATTENUATION":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&¶meters.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING","\tattribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR","\tattribute vec3 instanceColor;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT","\tattribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )","\tattribute vec4 color;","#elif defined( USE_COLOR )","\tattribute vec3 color;","#endif","#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )","\tattribute vec3 morphTarget0;","\tattribute vec3 morphTarget1;","\tattribute vec3 morphTarget2;","\tattribute vec3 morphTarget3;","\t#ifdef USE_MORPHNORMALS","\t\tattribute vec3 morphNormal0;","\t\tattribute vec3 morphNormal1;","\t\tattribute vec3 morphNormal2;","\t\tattribute vec3 morphNormal3;","\t#else","\t\tattribute vec3 morphTarget4;","\t\tattribute vec3 morphTarget5;","\t\tattribute vec3 morphTarget6;","\t\tattribute vec3 morphTarget7;","\t#endif","#endif","#ifdef USE_SKINNING","\tattribute vec4 skinIndex;","\tattribute vec4 skinWeight;","#endif","\n"].filter(filterEmptyLine).join("\n");prefixFragment=[customExtensions,generatePrecision(parameters),"#define SHADER_NAME "+parameters.shaderName,customDefines,parameters.useFog&¶meters.fog?"#define USE_FOG":"",parameters.useFog&¶meters.fogExp2?"#define FOG_EXP2":"",parameters.map?"#define USE_MAP":"",parameters.matcap?"#define USE_MATCAP":"",parameters.envMap?"#define USE_ENVMAP":"",parameters.envMap?"#define "+envMapTypeDefine:"",parameters.envMap?"#define "+envMapModeDefine:"",parameters.envMap?"#define "+envMapBlendingDefine:"",envMapCubeUVSize?"#define CUBEUV_TEXEL_WIDTH "+envMapCubeUVSize.texelWidth:"",envMapCubeUVSize?"#define CUBEUV_TEXEL_HEIGHT "+envMapCubeUVSize.texelHeight:"",envMapCubeUVSize?"#define CUBEUV_MAX_MIP "+envMapCubeUVSize.maxMip+".0":"",parameters.lightMap?"#define USE_LIGHTMAP":"",parameters.aoMap?"#define USE_AOMAP":"",parameters.emissiveMap?"#define USE_EMISSIVEMAP":"",parameters.bumpMap?"#define USE_BUMPMAP":"",parameters.normalMap?"#define USE_NORMALMAP":"",parameters.normalMap&¶meters.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",parameters.normalMap&¶meters.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",parameters.clearcoat?"#define USE_CLEARCOAT":"",parameters.clearcoatMap?"#define USE_CLEARCOATMAP":"",parameters.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",parameters.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",parameters.specularMap?"#define USE_SPECULARMAP":"",parameters.specularIntensityMap?"#define USE_SPECULARINTENSITYMAP":"",parameters.specularColorMap?"#define USE_SPECULARCOLORMAP":"",parameters.roughnessMap?"#define USE_ROUGHNESSMAP":"",parameters.metalnessMap?"#define USE_METALNESSMAP":"",parameters.alphaMap?"#define USE_ALPHAMAP":"",parameters.alphaTest?"#define USE_ALPHATEST":"",parameters.sheen?"#define USE_SHEEN":"",parameters.sheenColorMap?"#define USE_SHEENCOLORMAP":"",parameters.sheenRoughnessMap?"#define USE_SHEENROUGHNESSMAP":"",parameters.transmission?"#define USE_TRANSMISSION":"",parameters.transmissionMap?"#define USE_TRANSMISSIONMAP":"",parameters.thicknessMap?"#define USE_THICKNESSMAP":"",parameters.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",parameters.vertexTangents?"#define USE_TANGENT":"",parameters.vertexColors||parameters.instancingColor?"#define USE_COLOR":"",parameters.vertexAlphas?"#define USE_COLOR_ALPHA":"",parameters.vertexUvs?"#define USE_UV":"",parameters.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",parameters.gradientMap?"#define USE_GRADIENTMAP":"",parameters.flatShading?"#define FLAT_SHADED":"",parameters.doubleSided?"#define DOUBLE_SIDED":"",parameters.flipSided?"#define FLIP_SIDED":"",parameters.shadowMapEnabled?"#define USE_SHADOWMAP":"",parameters.shadowMapEnabled?"#define "+shadowMapTypeDefine:"",parameters.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",parameters.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",parameters.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",parameters.logarithmicDepthBuffer&¶meters.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",parameters.toneMapping!==NoToneMapping?"#define TONE_MAPPING":"",parameters.toneMapping!==NoToneMapping?ShaderChunk["tonemapping_pars_fragment"]:"",parameters.toneMapping!==NoToneMapping?getToneMappingFunction("toneMapping",parameters.toneMapping):"",parameters.dithering?"#define DITHERING":"",parameters.opaque?"#define OPAQUE":"",ShaderChunk["encodings_pars_fragment"],getTexelEncodingFunction("linearToOutputTexel",parameters.outputEncoding),parameters.depthPacking?"#define DEPTH_PACKING "+parameters.depthPacking:"","\n"].filter(filterEmptyLine).join("\n")}vertexShader=resolveIncludes(vertexShader);vertexShader=replaceLightNums(vertexShader,parameters);vertexShader=replaceClippingPlaneNums(vertexShader,parameters);fragmentShader=resolveIncludes(fragmentShader);fragmentShader=replaceLightNums(fragmentShader,parameters);fragmentShader=replaceClippingPlaneNums(fragmentShader,parameters);vertexShader=unrollLoops(vertexShader);fragmentShader=unrollLoops(fragmentShader);if(parameters.isWebGL2&¶meters.isRawShaderMaterial!==true){versionString="#version 300 es\n";prefixVertex=["precision mediump sampler2DArray;","#define attribute in","#define varying out","#define texture2D texture"].join("\n")+"\n"+prefixVertex;prefixFragment=["#define varying in",parameters.glslVersion===GLSL3?"":"layout(location = 0) out highp vec4 pc_fragColor;",parameters.glslVersion===GLSL3?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join("\n")+"\n"+prefixFragment}const vertexGlsl=versionString+prefixVertex+vertexShader;const fragmentGlsl=versionString+prefixFragment+fragmentShader;const glVertexShader=WebGLShader(gl,35633,vertexGlsl);const glFragmentShader=WebGLShader(gl,35632,fragmentGlsl);gl.attachShader(program,glVertexShader);gl.attachShader(program,glFragmentShader);if(parameters.index0AttributeName!==undefined){gl.bindAttribLocation(program,0,parameters.index0AttributeName)}else if(parameters.morphTargets===true){gl.bindAttribLocation(program,0,"position")}gl.linkProgram(program);if(renderer.debug.checkShaderErrors){const programLog=gl.getProgramInfoLog(program).trim();const vertexLog=gl.getShaderInfoLog(glVertexShader).trim();const fragmentLog=gl.getShaderInfoLog(glFragmentShader).trim();let runnable=true;let haveDiagnostics=true;if(gl.getProgramParameter(program,35714)===false){runnable=false;const vertexErrors=getShaderErrors(gl,glVertexShader,"vertex");const fragmentErrors=getShaderErrors(gl,glFragmentShader,"fragment");console.error("THREE.WebGLProgram: Shader Error "+gl.getError()+" - "+"VALIDATE_STATUS "+gl.getProgramParameter(program,35715)+"\n\n"+"Program Info Log: "+programLog+"\n"+vertexErrors+"\n"+fragmentErrors)}else if(programLog!==""){console.warn("THREE.WebGLProgram: Program Info Log:",programLog)}else if(vertexLog===""||fragmentLog===""){haveDiagnostics=false}if(haveDiagnostics){this.diagnostics={runnable:runnable,programLog:programLog,vertexShader:{log:vertexLog,prefix:prefixVertex},fragmentShader:{log:fragmentLog,prefix:prefixFragment}}}}gl.deleteShader(glVertexShader);gl.deleteShader(glFragmentShader);let cachedUniforms;this.getUniforms=function(){if(cachedUniforms===undefined){cachedUniforms=new WebGLUniforms(gl,program)}return cachedUniforms};let cachedAttributes;this.getAttributes=function(){if(cachedAttributes===undefined){cachedAttributes=fetchAttributeLocations(gl,program)}return cachedAttributes};this.destroy=function(){bindingStates.releaseStatesOfProgram(this);gl.deleteProgram(program);this.program=undefined};this.name=parameters.shaderName;this.id=programIdCount++;this.cacheKey=cacheKey;this.usedTimes=1;this.program=program;this.vertexShader=glVertexShader;this.fragmentShader=glFragmentShader;return this}let _id=0;class WebGLShaderCache{constructor(){this.shaderCache=new Map;this.materialCache=new Map}update(material){const vertexShader=material.vertexShader;const fragmentShader=material.fragmentShader;const vertexShaderStage=this._getShaderStage(vertexShader);const fragmentShaderStage=this._getShaderStage(fragmentShader);const materialShaders=this._getShaderCacheForMaterial(material);if(materialShaders.has(vertexShaderStage)===false){materialShaders.add(vertexShaderStage);vertexShaderStage.usedTimes++}if(materialShaders.has(fragmentShaderStage)===false){materialShaders.add(fragmentShaderStage);fragmentShaderStage.usedTimes++}return this}remove(material){const materialShaders=this.materialCache.get(material);for(const shaderStage of materialShaders){shaderStage.usedTimes--;if(shaderStage.usedTimes===0)this.shaderCache.delete(shaderStage.code)}this.materialCache.delete(material);return this}getVertexShaderID(material){return this._getShaderStage(material.vertexShader).id}getFragmentShaderID(material){return this._getShaderStage(material.fragmentShader).id}dispose(){this.shaderCache.clear();this.materialCache.clear()}_getShaderCacheForMaterial(material){const cache=this.materialCache;if(cache.has(material)===false){cache.set(material,new Set)}return cache.get(material)}_getShaderStage(code){const cache=this.shaderCache;if(cache.has(code)===false){const stage=new WebGLShaderStage(code);cache.set(code,stage)}return cache.get(code)}}class WebGLShaderStage{constructor(code){this.id=_id++;this.code=code;this.usedTimes=0}}function WebGLPrograms(renderer,cubemaps,cubeuvmaps,extensions,capabilities,bindingStates,clipping){const _programLayers=new Layers;const _customShaders=new WebGLShaderCache;const programs=[];const isWebGL2=capabilities.isWebGL2;const logarithmicDepthBuffer=capabilities.logarithmicDepthBuffer;const floatVertexTextures=capabilities.floatVertexTextures;const maxVertexUniforms=capabilities.maxVertexUniforms;const vertexTextures=capabilities.vertexTextures;let precision=capabilities.precision;const shaderIDs={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function getMaxBones(object){const skeleton=object.skeleton;const bones=skeleton.bones;if(floatVertexTextures){return 1024}else{const nVertexUniforms=maxVertexUniforms;const nVertexMatrices=Math.floor((nVertexUniforms-20)/4);const maxBones=Math.min(nVertexMatrices,bones.length);if(maxBones0;const useClearcoat=material.clearcoat>0;const parameters={isWebGL2:isWebGL2,shaderID:shaderID,shaderName:material.type,vertexShader:vertexShader,fragmentShader:fragmentShader,defines:material.defines,customVertexShaderID:customVertexShaderID,customFragmentShaderID:customFragmentShaderID,isRawShaderMaterial:material.isRawShaderMaterial===true,glslVersion:material.glslVersion,precision:precision,instancing:object.isInstancedMesh===true,instancingColor:object.isInstancedMesh===true&&object.instanceColor!==null,supportsVertexTextures:vertexTextures,outputEncoding:currentRenderTarget===null?renderer.outputEncoding:currentRenderTarget.isXRRenderTarget===true?currentRenderTarget.texture.encoding:LinearEncoding,map:!!material.map,matcap:!!material.matcap,envMap:!!envMap,envMapMode:envMap&&envMap.mapping,envMapCubeUVHeight:envMapCubeUVHeight,lightMap:!!material.lightMap,aoMap:!!material.aoMap,emissiveMap:!!material.emissiveMap,bumpMap:!!material.bumpMap,normalMap:!!material.normalMap,objectSpaceNormalMap:material.normalMapType===ObjectSpaceNormalMap,tangentSpaceNormalMap:material.normalMapType===TangentSpaceNormalMap,decodeVideoTexture:!!material.map&&material.map.isVideoTexture===true&&material.map.encoding===sRGBEncoding,clearcoat:useClearcoat,clearcoatMap:useClearcoat&&!!material.clearcoatMap,clearcoatRoughnessMap:useClearcoat&&!!material.clearcoatRoughnessMap,clearcoatNormalMap:useClearcoat&&!!material.clearcoatNormalMap,displacementMap:!!material.displacementMap,roughnessMap:!!material.roughnessMap,metalnessMap:!!material.metalnessMap,specularMap:!!material.specularMap,specularIntensityMap:!!material.specularIntensityMap,specularColorMap:!!material.specularColorMap,opaque:material.transparent===false&&material.blending===NormalBlending,alphaMap:!!material.alphaMap,alphaTest:useAlphaTest,gradientMap:!!material.gradientMap,sheen:material.sheen>0,sheenColorMap:!!material.sheenColorMap,sheenRoughnessMap:!!material.sheenRoughnessMap,transmission:material.transmission>0,transmissionMap:!!material.transmissionMap,thicknessMap:!!material.thicknessMap,combine:material.combine,vertexTangents:!!material.normalMap&&!!geometry.attributes.tangent,vertexColors:material.vertexColors,vertexAlphas:material.vertexColors===true&&!!geometry.attributes.color&&geometry.attributes.color.itemSize===4,vertexUvs:!!material.map||!!material.bumpMap||!!material.normalMap||!!material.specularMap||!!material.alphaMap||!!material.emissiveMap||!!material.roughnessMap||!!material.metalnessMap||!!material.clearcoatMap||!!material.clearcoatRoughnessMap||!!material.clearcoatNormalMap||!!material.displacementMap||!!material.transmissionMap||!!material.thicknessMap||!!material.specularIntensityMap||!!material.specularColorMap||!!material.sheenColorMap||!!material.sheenRoughnessMap,uvsVertexOnly:!(!!material.map||!!material.bumpMap||!!material.normalMap||!!material.specularMap||!!material.alphaMap||!!material.emissiveMap||!!material.roughnessMap||!!material.metalnessMap||!!material.clearcoatNormalMap||material.transmission>0||!!material.transmissionMap||!!material.thicknessMap||!!material.specularIntensityMap||!!material.specularColorMap||material.sheen>0||!!material.sheenColorMap||!!material.sheenRoughnessMap)&&!!material.displacementMap,fog:!!fog,useFog:material.fog,fogExp2:fog&&fog.isFogExp2,flatShading:!!material.flatShading,sizeAttenuation:material.sizeAttenuation,logarithmicDepthBuffer:logarithmicDepthBuffer,skinning:object.isSkinnedMesh===true&&maxBones>0,maxBones:maxBones,useVertexTexture:floatVertexTextures,morphTargets:geometry.morphAttributes.position!==undefined,morphNormals:geometry.morphAttributes.normal!==undefined,morphColors:geometry.morphAttributes.color!==undefined,morphTargetsCount:morphTargetsCount,morphTextureStride:morphTextureStride,numDirLights:lights.directional.length,numPointLights:lights.point.length,numSpotLights:lights.spot.length,numRectAreaLights:lights.rectArea.length,numHemiLights:lights.hemi.length,numDirLightShadows:lights.directionalShadowMap.length,numPointLightShadows:lights.pointShadowMap.length,numSpotLightShadows:lights.spotShadowMap.length,numClippingPlanes:clipping.numPlanes,numClipIntersection:clipping.numIntersection,dithering:material.dithering,shadowMapEnabled:renderer.shadowMap.enabled&&shadows.length>0,shadowMapType:renderer.shadowMap.type,toneMapping:material.toneMapped?renderer.toneMapping:NoToneMapping,physicallyCorrectLights:renderer.physicallyCorrectLights,premultipliedAlpha:material.premultipliedAlpha,doubleSided:material.side===DoubleSide,flipSided:material.side===BackSide,depthPacking:material.depthPacking!==undefined?material.depthPacking:false,index0AttributeName:material.index0AttributeName,extensionDerivatives:material.extensions&&material.extensions.derivatives,extensionFragDepth:material.extensions&&material.extensions.fragDepth,extensionDrawBuffers:material.extensions&&material.extensions.drawBuffers,extensionShaderTextureLOD:material.extensions&&material.extensions.shaderTextureLOD,rendererExtensionFragDepth:isWebGL2||extensions.has("EXT_frag_depth"),rendererExtensionDrawBuffers:isWebGL2||extensions.has("WEBGL_draw_buffers"),rendererExtensionShaderTextureLod:isWebGL2||extensions.has("EXT_shader_texture_lod"),customProgramCacheKey:material.customProgramCacheKey()};return parameters}function getProgramCacheKey(parameters){const array=[];if(parameters.shaderID){array.push(parameters.shaderID)}else{array.push(parameters.customVertexShaderID);array.push(parameters.customFragmentShaderID)}if(parameters.defines!==undefined){for(const name in parameters.defines){array.push(name);array.push(parameters.defines[name])}}if(parameters.isRawShaderMaterial===false){getProgramCacheKeyParameters(array,parameters);getProgramCacheKeyBooleans(array,parameters);array.push(renderer.outputEncoding)}array.push(parameters.customProgramCacheKey);return array.join()}function getProgramCacheKeyParameters(array,parameters){array.push(parameters.precision);array.push(parameters.outputEncoding);array.push(parameters.envMapMode);array.push(parameters.envMapCubeUVHeight);array.push(parameters.combine);array.push(parameters.vertexUvs);array.push(parameters.fogExp2);array.push(parameters.sizeAttenuation);array.push(parameters.maxBones);array.push(parameters.morphTargetsCount);array.push(parameters.morphAttributeCount);array.push(parameters.numDirLights);array.push(parameters.numPointLights);array.push(parameters.numSpotLights);array.push(parameters.numHemiLights);array.push(parameters.numRectAreaLights);array.push(parameters.numDirLightShadows);array.push(parameters.numPointLightShadows);array.push(parameters.numSpotLightShadows);array.push(parameters.shadowMapType);array.push(parameters.toneMapping);array.push(parameters.numClippingPlanes);array.push(parameters.numClipIntersection)}function getProgramCacheKeyBooleans(array,parameters){_programLayers.disableAll();if(parameters.isWebGL2)_programLayers.enable(0);if(parameters.supportsVertexTextures)_programLayers.enable(1);if(parameters.instancing)_programLayers.enable(2);if(parameters.instancingColor)_programLayers.enable(3);if(parameters.map)_programLayers.enable(4);if(parameters.matcap)_programLayers.enable(5);if(parameters.envMap)_programLayers.enable(6);if(parameters.lightMap)_programLayers.enable(7);if(parameters.aoMap)_programLayers.enable(8);if(parameters.emissiveMap)_programLayers.enable(9);if(parameters.bumpMap)_programLayers.enable(10);if(parameters.normalMap)_programLayers.enable(11);if(parameters.objectSpaceNormalMap)_programLayers.enable(12);if(parameters.tangentSpaceNormalMap)_programLayers.enable(13);if(parameters.clearcoat)_programLayers.enable(14);if(parameters.clearcoatMap)_programLayers.enable(15);if(parameters.clearcoatRoughnessMap)_programLayers.enable(16);if(parameters.clearcoatNormalMap)_programLayers.enable(17);if(parameters.displacementMap)_programLayers.enable(18);if(parameters.specularMap)_programLayers.enable(19);if(parameters.roughnessMap)_programLayers.enable(20);if(parameters.metalnessMap)_programLayers.enable(21);if(parameters.gradientMap)_programLayers.enable(22);if(parameters.alphaMap)_programLayers.enable(23);if(parameters.alphaTest)_programLayers.enable(24);if(parameters.vertexColors)_programLayers.enable(25);if(parameters.vertexAlphas)_programLayers.enable(26);if(parameters.vertexUvs)_programLayers.enable(27);if(parameters.vertexTangents)_programLayers.enable(28);if(parameters.uvsVertexOnly)_programLayers.enable(29);if(parameters.fog)_programLayers.enable(30);array.push(_programLayers.mask);_programLayers.disableAll();if(parameters.useFog)_programLayers.enable(0);if(parameters.flatShading)_programLayers.enable(1);if(parameters.logarithmicDepthBuffer)_programLayers.enable(2);if(parameters.skinning)_programLayers.enable(3);if(parameters.useVertexTexture)_programLayers.enable(4);if(parameters.morphTargets)_programLayers.enable(5);if(parameters.morphNormals)_programLayers.enable(6);if(parameters.morphColors)_programLayers.enable(7);if(parameters.premultipliedAlpha)_programLayers.enable(8);if(parameters.shadowMapEnabled)_programLayers.enable(9);if(parameters.physicallyCorrectLights)_programLayers.enable(10);if(parameters.doubleSided)_programLayers.enable(11);if(parameters.flipSided)_programLayers.enable(12);if(parameters.depthPacking)_programLayers.enable(13);if(parameters.dithering)_programLayers.enable(14);if(parameters.specularIntensityMap)_programLayers.enable(15);if(parameters.specularColorMap)_programLayers.enable(16);if(parameters.transmission)_programLayers.enable(17);if(parameters.transmissionMap)_programLayers.enable(18);if(parameters.thicknessMap)_programLayers.enable(19);if(parameters.sheen)_programLayers.enable(20);if(parameters.sheenColorMap)_programLayers.enable(21);if(parameters.sheenRoughnessMap)_programLayers.enable(22);if(parameters.decodeVideoTexture)_programLayers.enable(23);if(parameters.opaque)_programLayers.enable(24);array.push(_programLayers.mask)}function getUniforms(material){const shaderID=shaderIDs[material.type];let uniforms;if(shaderID){const shader=ShaderLib[shaderID];uniforms=UniformsUtils.clone(shader.uniforms)}else{uniforms=material.uniforms}return uniforms}function acquireProgram(parameters,cacheKey){let program;for(let p=0,pl=programs.length;p0){transmissive.push(renderItem)}else if(material.transparent===true){transparent.push(renderItem)}else{opaque.push(renderItem)}}function unshift(object,geometry,material,groupOrder,z,group){const renderItem=getNextRenderItem(object,geometry,material,groupOrder,z,group);if(material.transmission>0){transmissive.unshift(renderItem)}else if(material.transparent===true){transparent.unshift(renderItem)}else{opaque.unshift(renderItem)}}function sort(customOpaqueSort,customTransparentSort){if(opaque.length>1)opaque.sort(customOpaqueSort||painterSortStable);if(transmissive.length>1)transmissive.sort(customTransparentSort||reversePainterSortStable);if(transparent.length>1)transparent.sort(customTransparentSort||reversePainterSortStable)}function finish(){for(let i=renderItemsIndex,il=renderItems.length;i=lists.get(scene).length){list=new WebGLRenderList;lists.get(scene).push(list)}else{list=lists.get(scene)[renderCallDepth]}}return list}function dispose(){lists=new WeakMap}return{get:get,dispose:dispose}}function UniformsCache(){const lights={};return{get:function(light){if(lights[light.id]!==undefined){return lights[light.id]}let uniforms;switch(light.type){case"DirectionalLight":uniforms={direction:new Vector3,color:new Color};break;case"SpotLight":uniforms={position:new Vector3,direction:new Vector3,color:new Color,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":uniforms={position:new Vector3,color:new Color,distance:0,decay:0};break;case"HemisphereLight":uniforms={direction:new Vector3,skyColor:new Color,groundColor:new Color};break;case"RectAreaLight":uniforms={color:new Color,position:new Vector3,halfWidth:new Vector3,halfHeight:new Vector3};break}lights[light.id]=uniforms;return uniforms}}}function ShadowUniformsCache(){const lights={};return{get:function(light){if(lights[light.id]!==undefined){return lights[light.id]}let uniforms;switch(light.type){case"DirectionalLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2};break;case"SpotLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2};break;case"PointLight":uniforms={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new Vector2,shadowCameraNear:1,shadowCameraFar:1e3};break}lights[light.id]=uniforms;return uniforms}}}let nextVersion=0;function shadowCastingLightsFirst(lightA,lightB){return(lightB.castShadow?1:0)-(lightA.castShadow?1:0)}function WebGLLights(extensions,capabilities){const cache=new UniformsCache;const shadowCache=ShadowUniformsCache();const state={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotShadow:[],spotShadowMap:[],spotShadowMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[]};for(let i=0;i<9;i++)state.probe.push(new Vector3);const vector3=new Vector3;const matrix4=new Matrix4;const matrix42=new Matrix4;function setup(lights,physicallyCorrectLights){let r=0,g=0,b=0;for(let i=0;i<9;i++)state.probe[i].set(0,0,0);let directionalLength=0;let pointLength=0;let spotLength=0;let rectAreaLength=0;let hemiLength=0;let numDirectionalShadows=0;let numPointShadows=0;let numSpotShadows=0;lights.sort(shadowCastingLightsFirst);const scaleFactor=physicallyCorrectLights!==true?Math.PI:1;for(let i=0,l=lights.length;i0){if(capabilities.isWebGL2){state.rectAreaLTC1=UniformsLib.LTC_FLOAT_1;state.rectAreaLTC2=UniformsLib.LTC_FLOAT_2}else{if(extensions.has("OES_texture_float_linear")===true){state.rectAreaLTC1=UniformsLib.LTC_FLOAT_1;state.rectAreaLTC2=UniformsLib.LTC_FLOAT_2}else if(extensions.has("OES_texture_half_float_linear")===true){state.rectAreaLTC1=UniformsLib.LTC_HALF_1;state.rectAreaLTC2=UniformsLib.LTC_HALF_2}else{console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.")}}}state.ambient[0]=r;state.ambient[1]=g;state.ambient[2]=b;const hash=state.hash;if(hash.directionalLength!==directionalLength||hash.pointLength!==pointLength||hash.spotLength!==spotLength||hash.rectAreaLength!==rectAreaLength||hash.hemiLength!==hemiLength||hash.numDirectionalShadows!==numDirectionalShadows||hash.numPointShadows!==numPointShadows||hash.numSpotShadows!==numSpotShadows){state.directional.length=directionalLength;state.spot.length=spotLength;state.rectArea.length=rectAreaLength;state.point.length=pointLength;state.hemi.length=hemiLength;state.directionalShadow.length=numDirectionalShadows;state.directionalShadowMap.length=numDirectionalShadows;state.pointShadow.length=numPointShadows;state.pointShadowMap.length=numPointShadows;state.spotShadow.length=numSpotShadows;state.spotShadowMap.length=numSpotShadows;state.directionalShadowMatrix.length=numDirectionalShadows;state.pointShadowMatrix.length=numPointShadows;state.spotShadowMatrix.length=numSpotShadows;hash.directionalLength=directionalLength;hash.pointLength=pointLength;hash.spotLength=spotLength;hash.rectAreaLength=rectAreaLength;hash.hemiLength=hemiLength;hash.numDirectionalShadows=numDirectionalShadows;hash.numPointShadows=numPointShadows;hash.numSpotShadows=numSpotShadows;state.version=nextVersion++}}function setupView(lights,camera){let directionalLength=0;let pointLength=0;let spotLength=0;let rectAreaLength=0;let hemiLength=0;const viewMatrix=camera.matrixWorldInverse;for(let i=0,l=lights.length;i=renderStates.get(scene).length){renderState=new WebGLRenderState(extensions,capabilities);renderStates.get(scene).push(renderState)}else{renderState=renderStates.get(scene)[renderCallDepth]}}return renderState}function dispose(){renderStates=new WeakMap}return{get:get,dispose:dispose}}class MeshDepthMaterial extends Material{constructor(parameters){super();this.type="MeshDepthMaterial";this.depthPacking=BasicDepthPacking;this.map=null;this.alphaMap=null;this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.wireframe=false;this.wireframeLinewidth=1;this.fog=false;this.setValues(parameters)}copy(source){super.copy(source);this.depthPacking=source.depthPacking;this.map=source.map;this.alphaMap=source.alphaMap;this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;return this}}MeshDepthMaterial.prototype.isMeshDepthMaterial=true;class MeshDistanceMaterial extends Material{constructor(parameters){super();this.type="MeshDistanceMaterial";this.referencePosition=new Vector3;this.nearDistance=1;this.farDistance=1e3;this.map=null;this.alphaMap=null;this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.fog=false;this.setValues(parameters)}copy(source){super.copy(source);this.referencePosition.copy(source.referencePosition);this.nearDistance=source.nearDistance;this.farDistance=source.farDistance;this.map=source.map;this.alphaMap=source.alphaMap;this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;return this}}MeshDistanceMaterial.prototype.isMeshDistanceMaterial=true;const vertex="void main() {\n\tgl_Position = vec4( position, 1.0 );\n}";const fragment="uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}";function WebGLShadowMap(_renderer,_objects,_capabilities){let _frustum=new Frustum;const _shadowMapSize=new Vector2,_viewportSize=new Vector2,_viewport=new Vector4,_depthMaterial=new MeshDepthMaterial({depthPacking:RGBADepthPacking}),_distanceMaterial=new MeshDistanceMaterial,_materialCache={},_maxTextureSize=_capabilities.maxTextureSize;const shadowSide={0:BackSide,1:FrontSide,2:DoubleSide};const shadowMaterialVertical=new ShaderMaterial({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new Vector2},radius:{value:4}},vertexShader:vertex,fragmentShader:fragment});const shadowMaterialHorizontal=shadowMaterialVertical.clone();shadowMaterialHorizontal.defines.HORIZONTAL_PASS=1;const fullScreenTri=new BufferGeometry;fullScreenTri.setAttribute("position",new BufferAttribute(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const fullScreenMesh=new Mesh(fullScreenTri,shadowMaterialVertical);const scope=this;this.enabled=false;this.autoUpdate=true;this.needsUpdate=false;this.type=PCFShadowMap;this.render=function(lights,scene,camera){if(scope.enabled===false)return;if(scope.autoUpdate===false&&scope.needsUpdate===false)return;if(lights.length===0)return;const currentRenderTarget=_renderer.getRenderTarget();const activeCubeFace=_renderer.getActiveCubeFace();const activeMipmapLevel=_renderer.getActiveMipmapLevel();const _state=_renderer.state;_state.setBlending(NoBlending);_state.buffers.color.setClear(1,1,1,1);_state.buffers.depth.setTest(true);_state.setScissorTest(false);for(let i=0,il=lights.length;i_maxTextureSize||_shadowMapSize.y>_maxTextureSize){if(_shadowMapSize.x>_maxTextureSize){_viewportSize.x=Math.floor(_maxTextureSize/shadowFrameExtents.x);_shadowMapSize.x=_viewportSize.x*shadowFrameExtents.x;shadow.mapSize.x=_viewportSize.x}if(_shadowMapSize.y>_maxTextureSize){_viewportSize.y=Math.floor(_maxTextureSize/shadowFrameExtents.y);_shadowMapSize.y=_viewportSize.y*shadowFrameExtents.y;shadow.mapSize.y=_viewportSize.y}}if(shadow.map===null&&!shadow.isPointLightShadow&&this.type===VSMShadowMap){shadow.map=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y);shadow.map.texture.name=light.name+".shadowMap";shadow.mapPass=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y);shadow.camera.updateProjectionMatrix()}if(shadow.map===null){const pars={minFilter:NearestFilter,magFilter:NearestFilter,format:RGBAFormat};shadow.map=new WebGLRenderTarget(_shadowMapSize.x,_shadowMapSize.y,pars);shadow.map.texture.name=light.name+".shadowMap";shadow.camera.updateProjectionMatrix()}_renderer.setRenderTarget(shadow.map);_renderer.clear();const viewportCount=shadow.getViewportCount();for(let vp=0;vp0){const keyA=result.uuid,keyB=material.uuid;let materialsForVariant=_materialCache[keyA];if(materialsForVariant===undefined){materialsForVariant={};_materialCache[keyA]=materialsForVariant}let cachedMaterial=materialsForVariant[keyB];if(cachedMaterial===undefined){cachedMaterial=result.clone();materialsForVariant[keyB]=cachedMaterial}result=cachedMaterial}result.visible=material.visible;result.wireframe=material.wireframe;if(type===VSMShadowMap){result.side=material.shadowSide!==null?material.shadowSide:material.side}else{result.side=material.shadowSide!==null?material.shadowSide:shadowSide[material.side]}result.alphaMap=material.alphaMap;result.alphaTest=material.alphaTest;result.clipShadows=material.clipShadows;result.clippingPlanes=material.clippingPlanes;result.clipIntersection=material.clipIntersection;result.displacementMap=material.displacementMap;result.displacementScale=material.displacementScale;result.displacementBias=material.displacementBias;result.wireframeLinewidth=material.wireframeLinewidth;result.linewidth=material.linewidth;if(light.isPointLight===true&&result.isMeshDistanceMaterial===true){result.referencePosition.setFromMatrixPosition(light.matrixWorld);result.nearDistance=shadowCameraNear;result.farDistance=shadowCameraFar}return result}function renderObject(object,camera,shadowCamera,light,type){if(object.visible===false)return;const visible=object.layers.test(camera.layers);if(visible&&(object.isMesh||object.isLine||object.isPoints)){if((object.castShadow||object.receiveShadow&&type===VSMShadowMap)&&(!object.frustumCulled||_frustum.intersectsObject(object))){object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse,object.matrixWorld);const geometry=_objects.update(object);const material=object.material;if(Array.isArray(material)){const groups=geometry.groups;for(let k=0,kl=groups.length;k=1}else if(glVersion.indexOf("OpenGL ES")!==-1){version=parseFloat(/^OpenGL ES (\d)/.exec(glVersion)[1]);lineWidthAvailable=version>=2}let currentTextureSlot=null;let currentBoundTextures={};const scissorParam=gl.getParameter(3088);const viewportParam=gl.getParameter(2978);const currentScissor=(new Vector4).fromArray(scissorParam);const currentViewport=(new Vector4).fromArray(viewportParam);function createTexture(type,target,count){const data=new Uint8Array(4);const texture=gl.createTexture();gl.bindTexture(type,texture);gl.texParameteri(type,10241,9728);gl.texParameteri(type,10240,9728);for(let i=0;imaxSize||image.height>maxSize){scale=maxSize/Math.max(image.width,image.height)}if(scale<1||needsPowerOfTwo===true){if(typeof HTMLImageElement!=="undefined"&&image instanceof HTMLImageElement||typeof HTMLCanvasElement!=="undefined"&&image instanceof HTMLCanvasElement||typeof ImageBitmap!=="undefined"&&image instanceof ImageBitmap){const floor=needsPowerOfTwo?floorPowerOfTwo:Math.floor;const width=floor(scale*image.width);const height=floor(scale*image.height);if(_canvas===undefined)_canvas=createCanvas(width,height);const canvas=needsNewCanvas?createCanvas(width,height):_canvas;canvas.width=width;canvas.height=height;const context=canvas.getContext("2d");context.drawImage(image,0,0,width,height);console.warn("THREE.WebGLRenderer: Texture has been resized from ("+image.width+"x"+image.height+") to ("+width+"x"+height+").");return canvas}else{if("data"in image){console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+image.width+"x"+image.height+").")}return image}}return image}function isPowerOfTwo$1(image){return isPowerOfTwo(image.width)&&isPowerOfTwo(image.height)}function textureNeedsPowerOfTwo(texture){if(isWebGL2)return false;return texture.wrapS!==ClampToEdgeWrapping||texture.wrapT!==ClampToEdgeWrapping||texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter}function textureNeedsGenerateMipmaps(texture,supportsMips){return texture.generateMipmaps&&supportsMips&&texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter}function generateMipmap(target){_gl.generateMipmap(target)}function getInternalFormat(internalFormatName,glFormat,glType,encoding,isVideoTexture=false){if(isWebGL2===false)return glFormat;if(internalFormatName!==null){if(_gl[internalFormatName]!==undefined)return _gl[internalFormatName];console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format '"+internalFormatName+"'")}let internalFormat=glFormat;if(glFormat===6403){if(glType===5126)internalFormat=33326;if(glType===5131)internalFormat=33325;if(glType===5121)internalFormat=33321}if(glFormat===33319){if(glType===5126)internalFormat=33328;if(glType===5131)internalFormat=33327;if(glType===5121)internalFormat=33323}if(glFormat===6408){if(glType===5126)internalFormat=34836;if(glType===5131)internalFormat=34842;if(glType===5121)internalFormat=encoding===sRGBEncoding&&isVideoTexture===false?35907:32856;if(glType===32819)internalFormat=32854;if(glType===32820)internalFormat=32855}if(internalFormat===33325||internalFormat===33326||internalFormat===33327||internalFormat===33328||internalFormat===34842||internalFormat===34836){extensions.get("EXT_color_buffer_float")}return internalFormat}function getMipLevels(texture,image,supportsMips){if(textureNeedsGenerateMipmaps(texture,supportsMips)===true||texture.isFramebufferTexture&&texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter){return Math.log2(Math.max(image.width,image.height))+1}else if(texture.mipmaps!==undefined&&texture.mipmaps.length>0){return texture.mipmaps.length}else if(texture.isCompressedTexture&&Array.isArray(texture.image)){return image.mipmaps.length}else{return 1}}function filterFallback(f){if(f===NearestFilter||f===NearestMipmapNearestFilter||f===NearestMipmapLinearFilter){return 9728}return 9729}function onTextureDispose(event){const texture=event.target;texture.removeEventListener("dispose",onTextureDispose);deallocateTexture(texture);if(texture.isVideoTexture){_videoTextures.delete(texture)}}function onRenderTargetDispose(event){const renderTarget=event.target;renderTarget.removeEventListener("dispose",onRenderTargetDispose);deallocateRenderTarget(renderTarget)}function deallocateTexture(texture){const textureProperties=properties.get(texture);if(textureProperties.__webglInit===undefined)return;const source=texture.source;const webglTextures=_sources.get(source);if(webglTextures){const webglTexture=webglTextures[textureProperties.__cacheKey];webglTexture.usedTimes--;if(webglTexture.usedTimes===0){deleteTexture(texture)}if(Object.keys(webglTextures).length===0){_sources.delete(source)}}properties.remove(texture)}function deleteTexture(texture){const textureProperties=properties.get(texture);_gl.deleteTexture(textureProperties.__webglTexture);const source=texture.source;const webglTextures=_sources.get(source);delete webglTextures[textureProperties.__cacheKey];info.memory.textures--}function deallocateRenderTarget(renderTarget){const texture=renderTarget.texture;const renderTargetProperties=properties.get(renderTarget);const textureProperties=properties.get(texture);if(textureProperties.__webglTexture!==undefined){_gl.deleteTexture(textureProperties.__webglTexture);info.memory.textures--}if(renderTarget.depthTexture){renderTarget.depthTexture.dispose()}if(renderTarget.isWebGLCubeRenderTarget){for(let i=0;i<6;i++){_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]);if(renderTargetProperties.__webglDepthbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i])}}else{_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer);if(renderTargetProperties.__webglDepthbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer);if(renderTargetProperties.__webglMultisampledFramebuffer)_gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer);if(renderTargetProperties.__webglColorRenderbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer);if(renderTargetProperties.__webglDepthRenderbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer)}if(renderTarget.isWebGLMultipleRenderTargets){for(let i=0,il=texture.length;i=maxTextures){console.warn("THREE.WebGLTextures: Trying to use "+textureUnit+" texture units while this GPU supports only "+maxTextures)}textureUnits+=1;return textureUnit}function getTextureCacheKey(texture){const array=[];array.push(texture.wrapS);array.push(texture.wrapT);array.push(texture.magFilter);array.push(texture.minFilter);array.push(texture.anisotropy);array.push(texture.internalFormat);array.push(texture.format);array.push(texture.type);array.push(texture.generateMipmaps);array.push(texture.premultiplyAlpha);array.push(texture.flipY);array.push(texture.unpackAlignment);array.push(texture.encoding);return array.join()}function setTexture2D(texture,slot){const textureProperties=properties.get(texture);if(texture.isVideoTexture)updateVideoTexture(texture);if(texture.isRenderTargetTexture===false&&texture.version>0&&textureProperties.__version!==texture.version){const image=texture.image;if(image===null){console.warn("THREE.WebGLRenderer: Texture marked for update but no image data found.")}else if(image.complete===false){console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete")}else{uploadTexture(textureProperties,texture,slot);return}}state.activeTexture(33984+slot);state.bindTexture(3553,textureProperties.__webglTexture)}function setTexture2DArray(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(35866,textureProperties.__webglTexture)}function setTexture3D(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(32879,textureProperties.__webglTexture)}function setTextureCube(texture,slot){const textureProperties=properties.get(texture);if(texture.version>0&&textureProperties.__version!==texture.version){uploadCubeTexture(textureProperties,texture,slot);return}state.activeTexture(33984+slot);state.bindTexture(34067,textureProperties.__webglTexture)}const wrappingToGL={[RepeatWrapping]:10497,[ClampToEdgeWrapping]:33071,[MirroredRepeatWrapping]:33648};const filterToGL={[NearestFilter]:9728,[NearestMipmapNearestFilter]:9984,[NearestMipmapLinearFilter]:9986,[LinearFilter]:9729,[LinearMipmapNearestFilter]:9985,[LinearMipmapLinearFilter]:9987};function setTextureParameters(textureType,texture,supportsMips){if(supportsMips){_gl.texParameteri(textureType,10242,wrappingToGL[texture.wrapS]);_gl.texParameteri(textureType,10243,wrappingToGL[texture.wrapT]);if(textureType===32879||textureType===35866){_gl.texParameteri(textureType,32882,wrappingToGL[texture.wrapR])}_gl.texParameteri(textureType,10240,filterToGL[texture.magFilter]);_gl.texParameteri(textureType,10241,filterToGL[texture.minFilter])}else{_gl.texParameteri(textureType,10242,33071);_gl.texParameteri(textureType,10243,33071);if(textureType===32879||textureType===35866){_gl.texParameteri(textureType,32882,33071)}if(texture.wrapS!==ClampToEdgeWrapping||texture.wrapT!==ClampToEdgeWrapping){console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.")}_gl.texParameteri(textureType,10240,filterFallback(texture.magFilter));_gl.texParameteri(textureType,10241,filterFallback(texture.minFilter));if(texture.minFilter!==NearestFilter&&texture.minFilter!==LinearFilter){console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.")}}if(extensions.has("EXT_texture_filter_anisotropic")===true){const extension=extensions.get("EXT_texture_filter_anisotropic");if(texture.type===FloatType&&extensions.has("OES_texture_float_linear")===false)return;if(isWebGL2===false&&(texture.type===HalfFloatType&&extensions.has("OES_texture_half_float_linear")===false))return;if(texture.anisotropy>1||properties.get(texture).__currentAnisotropy){_gl.texParameterf(textureType,extension.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(texture.anisotropy,capabilities.getMaxAnisotropy()));properties.get(texture).__currentAnisotropy=texture.anisotropy}}}function initTexture(textureProperties,texture){let forceUpload=false;if(textureProperties.__webglInit===undefined){textureProperties.__webglInit=true;texture.addEventListener("dispose",onTextureDispose)}const source=texture.source;let webglTextures=_sources.get(source);if(webglTextures===undefined){webglTextures={};_sources.set(source,webglTextures)}const textureCacheKey=getTextureCacheKey(texture);if(textureCacheKey!==textureProperties.__cacheKey){if(webglTextures[textureCacheKey]===undefined){webglTextures[textureCacheKey]={texture:_gl.createTexture(),usedTimes:0};info.memory.textures++;forceUpload=true}webglTextures[textureCacheKey].usedTimes++;const webglTexture=webglTextures[textureProperties.__cacheKey];if(webglTexture!==undefined){webglTextures[textureProperties.__cacheKey].usedTimes--;if(webglTexture.usedTimes===0){deleteTexture(texture)}}textureProperties.__cacheKey=textureCacheKey;textureProperties.__webglTexture=webglTextures[textureCacheKey].texture}return forceUpload}function uploadTexture(textureProperties,texture,slot){let textureType=3553;if(texture.isDataArrayTexture)textureType=35866;if(texture.isData3DTexture)textureType=32879;const forceUpload=initTexture(textureProperties,texture);const source=texture.source;state.activeTexture(33984+slot);state.bindTexture(textureType,textureProperties.__webglTexture);if(source.version!==source.__currentVersion||forceUpload===true){_gl.pixelStorei(37440,texture.flipY);_gl.pixelStorei(37441,texture.premultiplyAlpha);_gl.pixelStorei(3317,texture.unpackAlignment);_gl.pixelStorei(37443,0);const needsPowerOfTwo=textureNeedsPowerOfTwo(texture)&&isPowerOfTwo$1(texture.image)===false;let image=resizeImage(texture.image,needsPowerOfTwo,false,maxTextureSize);image=verifyColorSpace(texture,image);const supportsMips=isPowerOfTwo$1(image)||isWebGL2,glFormat=utils.convert(texture.format,texture.encoding);let glType=utils.convert(texture.type),glInternalFormat=getInternalFormat(texture.internalFormat,glFormat,glType,texture.encoding,texture.isVideoTexture);setTextureParameters(textureType,texture,supportsMips);let mipmap;const mipmaps=texture.mipmaps;const useTexStorage=isWebGL2&&texture.isVideoTexture!==true;const allocateMemory=textureProperties.__version===undefined;const levels=getMipLevels(texture,image,supportsMips);if(texture.isDepthTexture){glInternalFormat=6402;if(isWebGL2){if(texture.type===FloatType){glInternalFormat=36012}else if(texture.type===UnsignedIntType){glInternalFormat=33190}else if(texture.type===UnsignedInt248Type){glInternalFormat=35056}else{glInternalFormat=33189}}else{if(texture.type===FloatType){console.error("WebGLRenderer: Floating point depth texture requires WebGL2.")}}if(texture.format===DepthFormat&&glInternalFormat===6402){if(texture.type!==UnsignedShortType&&texture.type!==UnsignedIntType){console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.");texture.type=UnsignedShortType;glType=utils.convert(texture.type)}}if(texture.format===DepthStencilFormat&&glInternalFormat===6402){glInternalFormat=34041;if(texture.type!==UnsignedInt248Type){console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.");texture.type=UnsignedInt248Type;glType=utils.convert(texture.type)}}if(useTexStorage&&allocateMemory){state.texStorage2D(3553,1,glInternalFormat,image.width,image.height)}else{state.texImage2D(3553,0,glInternalFormat,image.width,image.height,0,glFormat,glType,null)}}else if(texture.isDataTexture){if(mipmaps.length>0&&supportsMips){if(useTexStorage&&allocateMemory){state.texStorage2D(3553,levels,glInternalFormat,mipmaps[0].width,mipmaps[0].height)}for(let i=0,il=mipmaps.length;i0&&supportsMips){if(useTexStorage&&allocateMemory){state.texStorage2D(3553,levels,glInternalFormat,mipmaps[0].width,mipmaps[0].height)}for(let i=0,il=mipmaps.length;i0)levels++;state.texStorage2D(34067,levels,glInternalFormat,cubeImage[0].width,cubeImage[0].height)}for(let i=0;i<6;i++){if(isDataTexture){if(useTexStorage){state.texSubImage2D(34069+i,0,0,0,cubeImage[i].width,cubeImage[i].height,glFormat,glType,cubeImage[i].data)}else{state.texImage2D(34069+i,0,glInternalFormat,cubeImage[i].width,cubeImage[i].height,0,glFormat,glType,cubeImage[i].data)}for(let j=0;j0&&useMultisampledRTT(renderTarget)===false){renderTargetProperties.__webglMultisampledFramebuffer=_gl.createFramebuffer();renderTargetProperties.__webglColorRenderbuffer=_gl.createRenderbuffer();_gl.bindRenderbuffer(36161,renderTargetProperties.__webglColorRenderbuffer);const glFormat=utils.convert(texture.format,texture.encoding);const glType=utils.convert(texture.type);const glInternalFormat=getInternalFormat(texture.internalFormat,glFormat,glType,texture.encoding);const samples=getRenderTargetSamples(renderTarget);_gl.renderbufferStorageMultisample(36161,samples,glInternalFormat,renderTarget.width,renderTarget.height);state.bindFramebuffer(36160,renderTargetProperties.__webglMultisampledFramebuffer);_gl.framebufferRenderbuffer(36160,36064,36161,renderTargetProperties.__webglColorRenderbuffer);_gl.bindRenderbuffer(36161,null);if(renderTarget.depthBuffer){renderTargetProperties.__webglDepthRenderbuffer=_gl.createRenderbuffer();setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer,renderTarget,true)}state.bindFramebuffer(36160,null)}}if(isCube){state.bindTexture(34067,textureProperties.__webglTexture);setTextureParameters(34067,texture,supportsMips);for(let i=0;i<6;i++){setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i],renderTarget,texture,36064,34069+i)}if(textureNeedsGenerateMipmaps(texture,supportsMips)){generateMipmap(34067)}state.unbindTexture()}else if(isMultipleRenderTargets){const textures=renderTarget.texture;for(let i=0,il=textures.length;i0&&useMultisampledRTT(renderTarget)===false){const width=renderTarget.width;const height=renderTarget.height;let mask=16384;const invalidationArray=[36064];const depthStyle=renderTarget.stencilBuffer?33306:36096;if(renderTarget.depthBuffer){invalidationArray.push(depthStyle)}const renderTargetProperties=properties.get(renderTarget);const ignoreDepthValues=renderTargetProperties.__ignoreDepthValues!==undefined?renderTargetProperties.__ignoreDepthValues:false;if(ignoreDepthValues===false){if(renderTarget.depthBuffer)mask|=256;if(renderTarget.stencilBuffer)mask|=1024}state.bindFramebuffer(36008,renderTargetProperties.__webglMultisampledFramebuffer);state.bindFramebuffer(36009,renderTargetProperties.__webglFramebuffer);if(ignoreDepthValues===true){_gl.invalidateFramebuffer(36008,[depthStyle]);_gl.invalidateFramebuffer(36009,[depthStyle])}_gl.blitFramebuffer(0,0,width,height,0,0,width,height,mask,9728);if(supportsInvalidateFramebuffer){_gl.invalidateFramebuffer(36008,invalidationArray)}state.bindFramebuffer(36008,null);state.bindFramebuffer(36009,renderTargetProperties.__webglMultisampledFramebuffer)}}function getRenderTargetSamples(renderTarget){return Math.min(maxSamples,renderTarget.samples)}function useMultisampledRTT(renderTarget){const renderTargetProperties=properties.get(renderTarget);return isWebGL2&&renderTarget.samples>0&&extensions.has("WEBGL_multisampled_render_to_texture")===true&&renderTargetProperties.__useRenderToTexture!==false}function updateVideoTexture(texture){const frame=info.render.frame;if(_videoTextures.get(texture)!==frame){_videoTextures.set(texture,frame);texture.update()}}function verifyColorSpace(texture,image){const encoding=texture.encoding;const format=texture.format;const type=texture.type;if(texture.isCompressedTexture===true||texture.isVideoTexture===true||texture.format===_SRGBAFormat)return image;if(encoding!==LinearEncoding){if(encoding===sRGBEncoding){if(isWebGL2===false){if(extensions.has("EXT_sRGB")===true&&format===RGBAFormat){texture.format=_SRGBAFormat;texture.minFilter=LinearFilter;texture.generateMipmaps=false}else{image=ImageUtils.sRGBToLinear(image)}}else{if(format!==RGBAFormat||type!==UnsignedByteType){console.warn("THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.")}}}else{console.error("THREE.WebGLTextures: Unsupported texture encoding:",encoding)}}return image}this.allocateTextureUnit=allocateTextureUnit;this.resetTextureUnits=resetTextureUnits;this.setTexture2D=setTexture2D;this.setTexture2DArray=setTexture2DArray;this.setTexture3D=setTexture3D;this.setTextureCube=setTextureCube;this.rebindTextures=rebindTextures;this.setupRenderTarget=setupRenderTarget;this.updateRenderTargetMipmap=updateRenderTargetMipmap;this.updateMultisampleRenderTarget=updateMultisampleRenderTarget;this.setupDepthRenderbuffer=setupDepthRenderbuffer;this.setupFrameBufferTexture=setupFrameBufferTexture;this.useMultisampledRTT=useMultisampledRTT}function WebGLUtils(gl,extensions,capabilities){const isWebGL2=capabilities.isWebGL2;function convert(p,encoding=null){let extension;if(p===UnsignedByteType)return 5121;if(p===UnsignedShort4444Type)return 32819;if(p===UnsignedShort5551Type)return 32820;if(p===ByteType)return 5120;if(p===ShortType)return 5122;if(p===UnsignedShortType)return 5123;if(p===IntType)return 5124;if(p===UnsignedIntType)return 5125;if(p===FloatType)return 5126;if(p===HalfFloatType){if(isWebGL2)return 5131;extension=extensions.get("OES_texture_half_float");if(extension!==null){return extension.HALF_FLOAT_OES}else{return null}}if(p===AlphaFormat)return 6406;if(p===RGBAFormat)return 6408;if(p===LuminanceFormat)return 6409;if(p===LuminanceAlphaFormat)return 6410;if(p===DepthFormat)return 6402;if(p===DepthStencilFormat)return 34041;if(p===RedFormat)return 6403;if(p===RGBFormat){console.warn("THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228");return 6408}if(p===_SRGBAFormat){extension=extensions.get("EXT_sRGB");if(extension!==null){return extension.SRGB_ALPHA_EXT}else{return null}}if(p===RedIntegerFormat)return 36244;if(p===RGFormat)return 33319;if(p===RGIntegerFormat)return 33320;if(p===RGBAIntegerFormat)return 36249;if(p===RGB_S3TC_DXT1_Format||p===RGBA_S3TC_DXT1_Format||p===RGBA_S3TC_DXT3_Format||p===RGBA_S3TC_DXT5_Format){if(encoding===sRGBEncoding){extension=extensions.get("WEBGL_compressed_texture_s3tc_srgb");if(extension!==null){if(p===RGB_S3TC_DXT1_Format)return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(p===RGBA_S3TC_DXT1_Format)return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(p===RGBA_S3TC_DXT3_Format)return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(p===RGBA_S3TC_DXT5_Format)return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else{return null}}else{extension=extensions.get("WEBGL_compressed_texture_s3tc");if(extension!==null){if(p===RGB_S3TC_DXT1_Format)return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;if(p===RGBA_S3TC_DXT1_Format)return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(p===RGBA_S3TC_DXT3_Format)return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(p===RGBA_S3TC_DXT5_Format)return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT}else{return null}}}if(p===RGB_PVRTC_4BPPV1_Format||p===RGB_PVRTC_2BPPV1_Format||p===RGBA_PVRTC_4BPPV1_Format||p===RGBA_PVRTC_2BPPV1_Format){extension=extensions.get("WEBGL_compressed_texture_pvrtc");if(extension!==null){if(p===RGB_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(p===RGB_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(p===RGBA_PVRTC_4BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(p===RGBA_PVRTC_2BPPV1_Format)return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else{return null}}if(p===RGB_ETC1_Format){extension=extensions.get("WEBGL_compressed_texture_etc1");if(extension!==null){return extension.COMPRESSED_RGB_ETC1_WEBGL}else{return null}}if(p===RGB_ETC2_Format||p===RGBA_ETC2_EAC_Format){extension=extensions.get("WEBGL_compressed_texture_etc");if(extension!==null){if(p===RGB_ETC2_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ETC2:extension.COMPRESSED_RGB8_ETC2;if(p===RGBA_ETC2_EAC_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:extension.COMPRESSED_RGBA8_ETC2_EAC}else{return null}}if(p===RGBA_ASTC_4x4_Format||p===RGBA_ASTC_5x4_Format||p===RGBA_ASTC_5x5_Format||p===RGBA_ASTC_6x5_Format||p===RGBA_ASTC_6x6_Format||p===RGBA_ASTC_8x5_Format||p===RGBA_ASTC_8x6_Format||p===RGBA_ASTC_8x8_Format||p===RGBA_ASTC_10x5_Format||p===RGBA_ASTC_10x6_Format||p===RGBA_ASTC_10x8_Format||p===RGBA_ASTC_10x10_Format||p===RGBA_ASTC_12x10_Format||p===RGBA_ASTC_12x12_Format){extension=extensions.get("WEBGL_compressed_texture_astc");if(extension!==null){if(p===RGBA_ASTC_4x4_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:extension.COMPRESSED_RGBA_ASTC_4x4_KHR;if(p===RGBA_ASTC_5x4_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:extension.COMPRESSED_RGBA_ASTC_5x4_KHR;if(p===RGBA_ASTC_5x5_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:extension.COMPRESSED_RGBA_ASTC_5x5_KHR;if(p===RGBA_ASTC_6x5_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:extension.COMPRESSED_RGBA_ASTC_6x5_KHR;if(p===RGBA_ASTC_6x6_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:extension.COMPRESSED_RGBA_ASTC_6x6_KHR;if(p===RGBA_ASTC_8x5_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:extension.COMPRESSED_RGBA_ASTC_8x5_KHR;if(p===RGBA_ASTC_8x6_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:extension.COMPRESSED_RGBA_ASTC_8x6_KHR;if(p===RGBA_ASTC_8x8_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:extension.COMPRESSED_RGBA_ASTC_8x8_KHR;if(p===RGBA_ASTC_10x5_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:extension.COMPRESSED_RGBA_ASTC_10x5_KHR;if(p===RGBA_ASTC_10x6_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:extension.COMPRESSED_RGBA_ASTC_10x6_KHR;if(p===RGBA_ASTC_10x8_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:extension.COMPRESSED_RGBA_ASTC_10x8_KHR;if(p===RGBA_ASTC_10x10_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:extension.COMPRESSED_RGBA_ASTC_10x10_KHR;if(p===RGBA_ASTC_12x10_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:extension.COMPRESSED_RGBA_ASTC_12x10_KHR;if(p===RGBA_ASTC_12x12_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:extension.COMPRESSED_RGBA_ASTC_12x12_KHR}else{return null}}if(p===RGBA_BPTC_Format){extension=extensions.get("EXT_texture_compression_bptc");if(extension!==null){if(p===RGBA_BPTC_Format)return encoding===sRGBEncoding?extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:extension.COMPRESSED_RGBA_BPTC_UNORM_EXT}else{return null}}if(p===UnsignedInt248Type){if(isWebGL2)return 34042;extension=extensions.get("WEBGL_depth_texture");if(extension!==null){return extension.UNSIGNED_INT_24_8_WEBGL}else{return null}}}return{convert:convert}}class ArrayCamera extends PerspectiveCamera{constructor(array=[]){super();this.cameras=array}}ArrayCamera.prototype.isArrayCamera=true;class Group extends Object3D{constructor(){super();this.type="Group"}}Group.prototype.isGroup=true;const _moveEvent={type:"move"};class WebXRController{constructor(){this._targetRay=null;this._grip=null;this._hand=null}getHandSpace(){if(this._hand===null){this._hand=new Group;this._hand.matrixAutoUpdate=false;this._hand.visible=false;this._hand.joints={};this._hand.inputState={pinching:false}}return this._hand}getTargetRaySpace(){if(this._targetRay===null){this._targetRay=new Group;this._targetRay.matrixAutoUpdate=false;this._targetRay.visible=false;this._targetRay.hasLinearVelocity=false;this._targetRay.linearVelocity=new Vector3;this._targetRay.hasAngularVelocity=false;this._targetRay.angularVelocity=new Vector3}return this._targetRay}getGripSpace(){if(this._grip===null){this._grip=new Group;this._grip.matrixAutoUpdate=false;this._grip.visible=false;this._grip.hasLinearVelocity=false;this._grip.linearVelocity=new Vector3;this._grip.hasAngularVelocity=false;this._grip.angularVelocity=new Vector3}return this._grip}dispatchEvent(event){if(this._targetRay!==null){this._targetRay.dispatchEvent(event)}if(this._grip!==null){this._grip.dispatchEvent(event)}if(this._hand!==null){this._hand.dispatchEvent(event)}return this}disconnect(inputSource){this.dispatchEvent({type:"disconnected",data:inputSource});if(this._targetRay!==null){this._targetRay.visible=false}if(this._grip!==null){this._grip.visible=false}if(this._hand!==null){this._hand.visible=false}return this}update(inputSource,frame,referenceSpace){let inputPose=null;let gripPose=null;let handPose=null;const targetRay=this._targetRay;const grip=this._grip;const hand=this._hand;if(inputSource&&frame.session.visibilityState!=="visible-blurred"){if(targetRay!==null){inputPose=frame.getPose(inputSource.targetRaySpace,referenceSpace);if(inputPose!==null){targetRay.matrix.fromArray(inputPose.transform.matrix);targetRay.matrix.decompose(targetRay.position,targetRay.rotation,targetRay.scale);if(inputPose.linearVelocity){targetRay.hasLinearVelocity=true;targetRay.linearVelocity.copy(inputPose.linearVelocity)}else{targetRay.hasLinearVelocity=false}if(inputPose.angularVelocity){targetRay.hasAngularVelocity=true;targetRay.angularVelocity.copy(inputPose.angularVelocity)}else{targetRay.hasAngularVelocity=false}this.dispatchEvent(_moveEvent)}}if(hand&&inputSource.hand){handPose=true;for(const inputjoint of inputSource.hand.values()){const jointPose=frame.getJointPose(inputjoint,referenceSpace);if(hand.joints[inputjoint.jointName]===undefined){const joint=new Group;joint.matrixAutoUpdate=false;joint.visible=false;hand.joints[inputjoint.jointName]=joint;hand.add(joint)}const joint=hand.joints[inputjoint.jointName];if(jointPose!==null){joint.matrix.fromArray(jointPose.transform.matrix);joint.matrix.decompose(joint.position,joint.rotation,joint.scale);joint.jointRadius=jointPose.radius}joint.visible=jointPose!==null}const indexTip=hand.joints["index-finger-tip"];const thumbTip=hand.joints["thumb-tip"];const distance=indexTip.position.distanceTo(thumbTip.position);const distanceToPinch=.02;const threshold=.005;if(hand.inputState.pinching&&distance>distanceToPinch+threshold){hand.inputState.pinching=false;this.dispatchEvent({type:"pinchend",handedness:inputSource.handedness,target:this})}else if(!hand.inputState.pinching&&distance<=distanceToPinch-threshold){hand.inputState.pinching=true;this.dispatchEvent({type:"pinchstart",handedness:inputSource.handedness,target:this})}}else{if(grip!==null&&inputSource.gripSpace){gripPose=frame.getPose(inputSource.gripSpace,referenceSpace);if(gripPose!==null){grip.matrix.fromArray(gripPose.transform.matrix);grip.matrix.decompose(grip.position,grip.rotation,grip.scale);if(gripPose.linearVelocity){grip.hasLinearVelocity=true;grip.linearVelocity.copy(gripPose.linearVelocity)}else{grip.hasLinearVelocity=false}if(gripPose.angularVelocity){grip.hasAngularVelocity=true;grip.angularVelocity.copy(gripPose.angularVelocity)}else{grip.hasAngularVelocity=false}}}}}if(targetRay!==null){targetRay.visible=inputPose!==null}if(grip!==null){grip.visible=gripPose!==null}if(hand!==null){hand.visible=handPose!==null}return this}}class DepthTexture extends Texture{constructor(width,height,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy,format){format=format!==undefined?format:DepthFormat;if(format!==DepthFormat&&format!==DepthStencilFormat){throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat")}if(type===undefined&&format===DepthFormat)type=UnsignedShortType;if(type===undefined&&format===DepthStencilFormat)type=UnsignedInt248Type;super(null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.image={width:width,height:height};this.magFilter=magFilter!==undefined?magFilter:NearestFilter;this.minFilter=minFilter!==undefined?minFilter:NearestFilter;this.flipY=false;this.generateMipmaps=false}}DepthTexture.prototype.isDepthTexture=true;class WebXRManager extends EventDispatcher$1{constructor(renderer,gl){super();const scope=this;let session=null;let framebufferScaleFactor=1;let referenceSpace=null;let referenceSpaceType="local-floor";let pose=null;let glBinding=null;let glProjLayer=null;let glBaseLayer=null;let xrFrame=null;const attributes=gl.getContextAttributes();let initialRenderTarget=null;let newRenderTarget=null;const controllers=[];const inputSourcesMap=new Map;const cameraL=new PerspectiveCamera;cameraL.layers.enable(1);cameraL.viewport=new Vector4;const cameraR=new PerspectiveCamera;cameraR.layers.enable(2);cameraR.viewport=new Vector4;const cameras=[cameraL,cameraR];const cameraVR=new ArrayCamera;cameraVR.layers.enable(1);cameraVR.layers.enable(2);let _currentDepthNear=null;let _currentDepthFar=null;this.cameraAutoUpdate=true;this.enabled=false;this.isPresenting=false;this.getController=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getTargetRaySpace()};this.getControllerGrip=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getGripSpace()};this.getHand=function(index){let controller=controllers[index];if(controller===undefined){controller=new WebXRController;controllers[index]=controller}return controller.getHandSpace()};function onSessionEvent(event){const controller=inputSourcesMap.get(event.inputSource);if(controller){controller.dispatchEvent({type:event.type,data:event.inputSource})}}function onSessionEnd(){inputSourcesMap.forEach((function(controller,inputSource){controller.disconnect(inputSource)}));inputSourcesMap.clear();_currentDepthNear=null;_currentDepthFar=null;renderer.setRenderTarget(initialRenderTarget);glBaseLayer=null;glProjLayer=null;glBinding=null;session=null;newRenderTarget=null;animation.stop();scope.isPresenting=false;scope.dispatchEvent({type:"sessionend"})}this.setFramebufferScaleFactor=function(value){framebufferScaleFactor=value;if(scope.isPresenting===true){console.warn("THREE.WebXRManager: Cannot change framebuffer scale while presenting.")}};this.setReferenceSpaceType=function(value){referenceSpaceType=value;if(scope.isPresenting===true){console.warn("THREE.WebXRManager: Cannot change reference space type while presenting.")}};this.getReferenceSpace=function(){return referenceSpace};this.getBaseLayer=function(){return glProjLayer!==null?glProjLayer:glBaseLayer};this.getBinding=function(){return glBinding};this.getFrame=function(){return xrFrame};this.getSession=function(){return session};this.setSession=async function(value){session=value;if(session!==null){initialRenderTarget=renderer.getRenderTarget();session.addEventListener("select",onSessionEvent);session.addEventListener("selectstart",onSessionEvent);session.addEventListener("selectend",onSessionEvent);session.addEventListener("squeeze",onSessionEvent);session.addEventListener("squeezestart",onSessionEvent);session.addEventListener("squeezeend",onSessionEvent);session.addEventListener("end",onSessionEnd);session.addEventListener("inputsourceschange",onInputSourcesChange);if(attributes.xrCompatible!==true){await gl.makeXRCompatible()}if(session.renderState.layers===undefined||renderer.capabilities.isWebGL2===false){const layerInit={antialias:session.renderState.layers===undefined?attributes.antialias:true,alpha:attributes.alpha,depth:attributes.depth,stencil:attributes.stencil,framebufferScaleFactor:framebufferScaleFactor};glBaseLayer=new XRWebGLLayer(session,gl,layerInit);session.updateRenderState({baseLayer:glBaseLayer});newRenderTarget=new WebGLRenderTarget(glBaseLayer.framebufferWidth,glBaseLayer.framebufferHeight,{format:RGBAFormat,type:UnsignedByteType,encoding:renderer.outputEncoding})}else{let depthFormat=null;let depthType=null;let glDepthFormat=null;if(attributes.depth){glDepthFormat=attributes.stencil?35056:33190;depthFormat=attributes.stencil?DepthStencilFormat:DepthFormat;depthType=attributes.stencil?UnsignedInt248Type:UnsignedShortType}const projectionlayerInit={colorFormat:renderer.outputEncoding===sRGBEncoding?35907:32856,depthFormat:glDepthFormat,scaleFactor:framebufferScaleFactor};glBinding=new XRWebGLBinding(session,gl);glProjLayer=glBinding.createProjectionLayer(projectionlayerInit);session.updateRenderState({layers:[glProjLayer]});newRenderTarget=new WebGLRenderTarget(glProjLayer.textureWidth,glProjLayer.textureHeight,{format:RGBAFormat,type:UnsignedByteType,depthTexture:new DepthTexture(glProjLayer.textureWidth,glProjLayer.textureHeight,depthType,undefined,undefined,undefined,undefined,undefined,undefined,depthFormat),stencilBuffer:attributes.stencil,encoding:renderer.outputEncoding,samples:attributes.antialias?4:0});const renderTargetProperties=renderer.properties.get(newRenderTarget);renderTargetProperties.__ignoreDepthValues=glProjLayer.ignoreDepthValues}newRenderTarget.isXRRenderTarget=true;this.setFoveation(1);referenceSpace=await session.requestReferenceSpace(referenceSpaceType);animation.setContext(session);animation.start();scope.isPresenting=true;scope.dispatchEvent({type:"sessionstart"})}};function onInputSourcesChange(event){const inputSources=session.inputSources;for(let i=0;i0){uniforms.alphaTest.value=material.alphaTest}const envMap=properties.get(material).envMap;if(envMap){uniforms.envMap.value=envMap;uniforms.flipEnvMap.value=envMap.isCubeTexture&&envMap.isRenderTargetTexture===false?-1:1;uniforms.reflectivity.value=material.reflectivity;uniforms.ior.value=material.ior;uniforms.refractionRatio.value=material.refractionRatio}if(material.lightMap){uniforms.lightMap.value=material.lightMap;const scaleFactor=renderer.physicallyCorrectLights!==true?Math.PI:1;uniforms.lightMapIntensity.value=material.lightMapIntensity*scaleFactor}if(material.aoMap){uniforms.aoMap.value=material.aoMap;uniforms.aoMapIntensity.value=material.aoMapIntensity}let uvScaleMap;if(material.map){uvScaleMap=material.map}else if(material.specularMap){uvScaleMap=material.specularMap}else if(material.displacementMap){uvScaleMap=material.displacementMap}else if(material.normalMap){uvScaleMap=material.normalMap}else if(material.bumpMap){uvScaleMap=material.bumpMap}else if(material.roughnessMap){uvScaleMap=material.roughnessMap}else if(material.metalnessMap){uvScaleMap=material.metalnessMap}else if(material.alphaMap){uvScaleMap=material.alphaMap}else if(material.emissiveMap){uvScaleMap=material.emissiveMap}else if(material.clearcoatMap){uvScaleMap=material.clearcoatMap}else if(material.clearcoatNormalMap){uvScaleMap=material.clearcoatNormalMap}else if(material.clearcoatRoughnessMap){uvScaleMap=material.clearcoatRoughnessMap}else if(material.specularIntensityMap){uvScaleMap=material.specularIntensityMap}else if(material.specularColorMap){uvScaleMap=material.specularColorMap}else if(material.transmissionMap){uvScaleMap=material.transmissionMap}else if(material.thicknessMap){uvScaleMap=material.thicknessMap}else if(material.sheenColorMap){uvScaleMap=material.sheenColorMap}else if(material.sheenRoughnessMap){uvScaleMap=material.sheenRoughnessMap}if(uvScaleMap!==undefined){if(uvScaleMap.isWebGLRenderTarget){uvScaleMap=uvScaleMap.texture}if(uvScaleMap.matrixAutoUpdate===true){uvScaleMap.updateMatrix()}uniforms.uvTransform.value.copy(uvScaleMap.matrix)}let uv2ScaleMap;if(material.aoMap){uv2ScaleMap=material.aoMap}else if(material.lightMap){uv2ScaleMap=material.lightMap}if(uv2ScaleMap!==undefined){if(uv2ScaleMap.isWebGLRenderTarget){uv2ScaleMap=uv2ScaleMap.texture}if(uv2ScaleMap.matrixAutoUpdate===true){uv2ScaleMap.updateMatrix()}uniforms.uv2Transform.value.copy(uv2ScaleMap.matrix)}}function refreshUniformsLine(uniforms,material){uniforms.diffuse.value.copy(material.color);uniforms.opacity.value=material.opacity}function refreshUniformsDash(uniforms,material){uniforms.dashSize.value=material.dashSize;uniforms.totalSize.value=material.dashSize+material.gapSize;uniforms.scale.value=material.scale}function refreshUniformsPoints(uniforms,material,pixelRatio,height){uniforms.diffuse.value.copy(material.color);uniforms.opacity.value=material.opacity;uniforms.size.value=material.size*pixelRatio;uniforms.scale.value=height*.5;if(material.map){uniforms.map.value=material.map}if(material.alphaMap){uniforms.alphaMap.value=material.alphaMap}if(material.alphaTest>0){uniforms.alphaTest.value=material.alphaTest}let uvScaleMap;if(material.map){uvScaleMap=material.map}else if(material.alphaMap){uvScaleMap=material.alphaMap}if(uvScaleMap!==undefined){if(uvScaleMap.matrixAutoUpdate===true){uvScaleMap.updateMatrix()}uniforms.uvTransform.value.copy(uvScaleMap.matrix)}}function refreshUniformsSprites(uniforms,material){uniforms.diffuse.value.copy(material.color);uniforms.opacity.value=material.opacity;uniforms.rotation.value=material.rotation;if(material.map){uniforms.map.value=material.map}if(material.alphaMap){uniforms.alphaMap.value=material.alphaMap}if(material.alphaTest>0){uniforms.alphaTest.value=material.alphaTest}let uvScaleMap;if(material.map){uvScaleMap=material.map}else if(material.alphaMap){uvScaleMap=material.alphaMap}if(uvScaleMap!==undefined){if(uvScaleMap.matrixAutoUpdate===true){uvScaleMap.updateMatrix()}uniforms.uvTransform.value.copy(uvScaleMap.matrix)}}function refreshUniformsPhong(uniforms,material){uniforms.specular.value.copy(material.specular);uniforms.shininess.value=Math.max(material.shininess,1e-4)}function refreshUniformsToon(uniforms,material){if(material.gradientMap){uniforms.gradientMap.value=material.gradientMap}}function refreshUniformsStandard(uniforms,material){uniforms.roughness.value=material.roughness;uniforms.metalness.value=material.metalness;if(material.roughnessMap){uniforms.roughnessMap.value=material.roughnessMap}if(material.metalnessMap){uniforms.metalnessMap.value=material.metalnessMap}const envMap=properties.get(material).envMap;if(envMap){uniforms.envMapIntensity.value=material.envMapIntensity}}function refreshUniformsPhysical(uniforms,material,transmissionRenderTarget){uniforms.ior.value=material.ior;if(material.sheen>0){uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen);uniforms.sheenRoughness.value=material.sheenRoughness;if(material.sheenColorMap){uniforms.sheenColorMap.value=material.sheenColorMap}if(material.sheenRoughnessMap){uniforms.sheenRoughnessMap.value=material.sheenRoughnessMap}}if(material.clearcoat>0){uniforms.clearcoat.value=material.clearcoat;uniforms.clearcoatRoughness.value=material.clearcoatRoughness;if(material.clearcoatMap){uniforms.clearcoatMap.value=material.clearcoatMap}if(material.clearcoatRoughnessMap){uniforms.clearcoatRoughnessMap.value=material.clearcoatRoughnessMap}if(material.clearcoatNormalMap){uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale);uniforms.clearcoatNormalMap.value=material.clearcoatNormalMap;if(material.side===BackSide){uniforms.clearcoatNormalScale.value.negate()}}}if(material.transmission>0){uniforms.transmission.value=material.transmission;uniforms.transmissionSamplerMap.value=transmissionRenderTarget.texture;uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width,transmissionRenderTarget.height);if(material.transmissionMap){uniforms.transmissionMap.value=material.transmissionMap}uniforms.thickness.value=material.thickness;if(material.thicknessMap){uniforms.thicknessMap.value=material.thicknessMap}uniforms.attenuationDistance.value=material.attenuationDistance;uniforms.attenuationColor.value.copy(material.attenuationColor)}uniforms.specularIntensity.value=material.specularIntensity;uniforms.specularColor.value.copy(material.specularColor);if(material.specularIntensityMap){uniforms.specularIntensityMap.value=material.specularIntensityMap}if(material.specularColorMap){uniforms.specularColorMap.value=material.specularColorMap}}function refreshUniformsMatcap(uniforms,material){if(material.matcap){uniforms.matcap.value=material.matcap}}function refreshUniformsDistance(uniforms,material){uniforms.referencePosition.value.copy(material.referencePosition);uniforms.nearDistance.value=material.nearDistance;uniforms.farDistance.value=material.farDistance}return{refreshFogUniforms:refreshFogUniforms,refreshMaterialUniforms:refreshMaterialUniforms}}function createCanvasElement(){const canvas=createElementNS("canvas");canvas.style.display="block";return canvas}function WebGLRenderer(parameters={}){const _canvas=parameters.canvas!==undefined?parameters.canvas:createCanvasElement(),_context=parameters.context!==undefined?parameters.context:null,_depth=parameters.depth!==undefined?parameters.depth:true,_stencil=parameters.stencil!==undefined?parameters.stencil:true,_antialias=parameters.antialias!==undefined?parameters.antialias:false,_premultipliedAlpha=parameters.premultipliedAlpha!==undefined?parameters.premultipliedAlpha:true,_preserveDrawingBuffer=parameters.preserveDrawingBuffer!==undefined?parameters.preserveDrawingBuffer:false,_powerPreference=parameters.powerPreference!==undefined?parameters.powerPreference:"default",_failIfMajorPerformanceCaveat=parameters.failIfMajorPerformanceCaveat!==undefined?parameters.failIfMajorPerformanceCaveat:false;let _alpha;if(parameters.context!==undefined){_alpha=_context.getContextAttributes().alpha}else{_alpha=parameters.alpha!==undefined?parameters.alpha:false}let currentRenderList=null;let currentRenderState=null;const renderListStack=[];const renderStateStack=[];this.domElement=_canvas;this.debug={checkShaderErrors:true};this.autoClear=true;this.autoClearColor=true;this.autoClearDepth=true;this.autoClearStencil=true;this.sortObjects=true;this.clippingPlanes=[];this.localClippingEnabled=false;this.outputEncoding=LinearEncoding;this.physicallyCorrectLights=false;this.toneMapping=NoToneMapping;this.toneMappingExposure=1;const _this=this;let _isContextLost=false;let _currentActiveCubeFace=0;let _currentActiveMipmapLevel=0;let _currentRenderTarget=null;let _currentMaterialId=-1;let _currentCamera=null;const _currentViewport=new Vector4;const _currentScissor=new Vector4;let _currentScissorTest=null;let _width=_canvas.width;let _height=_canvas.height;let _pixelRatio=1;let _opaqueSort=null;let _transparentSort=null;const _viewport=new Vector4(0,0,_width,_height);const _scissor=new Vector4(0,0,_width,_height);let _scissorTest=false;const _frustum=new Frustum;let _clippingEnabled=false;let _localClippingEnabled=false;let _transmissionRenderTarget=null;const _projScreenMatrix=new Matrix4;const _vector2=new Vector2;const _vector3=new Vector3;const _emptyScene={background:null,fog:null,environment:null,overrideMaterial:null,isScene:true};function getTargetPixelRatio(){return _currentRenderTarget===null?_pixelRatio:1}let _gl=_context;function getContext(contextNames,contextAttributes){for(let i=0;i0){currentRenderState=renderStateStack[renderStateStack.length-1]}else{currentRenderState=null}renderListStack.pop();if(renderListStack.length>0){currentRenderList=renderListStack[renderListStack.length-1]}else{currentRenderList=null}};function projectObject(object,camera,groupOrder,sortObjects){if(object.visible===false)return;const visible=object.layers.test(camera.layers);if(visible){if(object.isGroup){groupOrder=object.renderOrder}else if(object.isLOD){if(object.autoUpdate===true)object.update(camera)}else if(object.isLight){currentRenderState.pushLight(object);if(object.castShadow){currentRenderState.pushShadow(object)}}else if(object.isSprite){if(!object.frustumCulled||_frustum.intersectsSprite(object)){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix)}const geometry=objects.update(object);const material=object.material;if(material.visible){currentRenderList.push(object,geometry,material,groupOrder,_vector3.z,null)}}}else if(object.isMesh||object.isLine||object.isPoints){if(object.isSkinnedMesh){if(object.skeleton.frame!==info.render.frame){object.skeleton.update();object.skeleton.frame=info.render.frame}}if(!object.frustumCulled||_frustum.intersectsObject(object)){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix)}const geometry=objects.update(object);const material=object.material;if(Array.isArray(material)){const groups=geometry.groups;for(let i=0,l=groups.length;i0)renderTransmissionPass(opaqueObjects,scene,camera);if(viewport)state.viewport(_currentViewport.copy(viewport));if(opaqueObjects.length>0)renderObjects(opaqueObjects,scene,camera);if(transmissiveObjects.length>0)renderObjects(transmissiveObjects,scene,camera);if(transparentObjects.length>0)renderObjects(transparentObjects,scene,camera);state.buffers.depth.setTest(true);state.buffers.depth.setMask(true);state.buffers.color.setMask(true);state.setPolygonOffset(false)}function renderTransmissionPass(opaqueObjects,scene,camera){const isWebGL2=capabilities.isWebGL2;if(_transmissionRenderTarget===null){_transmissionRenderTarget=new WebGLRenderTarget(1,1,{generateMipmaps:true,type:utils.convert(HalfFloatType)!==null?HalfFloatType:UnsignedByteType,minFilter:LinearMipmapLinearFilter,samples:isWebGL2&&_antialias===true?4:0})}_this.getDrawingBufferSize(_vector2);if(isWebGL2){_transmissionRenderTarget.setSize(_vector2.x,_vector2.y)}else{_transmissionRenderTarget.setSize(floorPowerOfTwo(_vector2.x),floorPowerOfTwo(_vector2.y))}const currentRenderTarget=_this.getRenderTarget();_this.setRenderTarget(_transmissionRenderTarget);_this.clear();const currentToneMapping=_this.toneMapping;_this.toneMapping=NoToneMapping;renderObjects(opaqueObjects,scene,camera);_this.toneMapping=currentToneMapping;textures.updateMultisampleRenderTarget(_transmissionRenderTarget);textures.updateRenderTargetMipmap(_transmissionRenderTarget);_this.setRenderTarget(currentRenderTarget)}function renderObjects(renderList,scene,camera){const overrideMaterial=scene.isScene===true?scene.overrideMaterial:null;for(let i=0,l=renderList.length;i0&&textures.useMultisampledRTT(renderTarget)===false){framebuffer=properties.get(renderTarget).__webglMultisampledFramebuffer}else{framebuffer=__webglFramebuffer}_currentViewport.copy(renderTarget.viewport);_currentScissor.copy(renderTarget.scissor);_currentScissorTest=renderTarget.scissorTest}else{_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor();_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor();_currentScissorTest=_scissorTest}const framebufferBound=state.bindFramebuffer(36160,framebuffer);if(framebufferBound&&capabilities.drawBuffers&&useDefaultFramebuffer){state.drawBuffers(renderTarget,framebuffer)}state.viewport(_currentViewport);state.scissor(_currentScissor);state.setScissorTest(_currentScissorTest);if(isCube){const textureProperties=properties.get(renderTarget.texture);_gl.framebufferTexture2D(36160,36064,34069+activeCubeFace,textureProperties.__webglTexture,activeMipmapLevel)}else if(isRenderTarget3D){const textureProperties=properties.get(renderTarget.texture);const layer=activeCubeFace||0;_gl.framebufferTextureLayer(36160,36064,textureProperties.__webglTexture,activeMipmapLevel||0,layer)}_currentMaterialId=-1};this.readRenderTargetPixels=function(renderTarget,x,y,width,height,buffer,activeCubeFaceIndex){if(!(renderTarget&&renderTarget.isWebGLRenderTarget)){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");return}let framebuffer=properties.get(renderTarget).__webglFramebuffer;if(renderTarget.isWebGLCubeRenderTarget&&activeCubeFaceIndex!==undefined){framebuffer=framebuffer[activeCubeFaceIndex]}if(framebuffer){state.bindFramebuffer(36160,framebuffer);try{const texture=renderTarget.texture;const textureFormat=texture.format;const textureType=texture.type;if(textureFormat!==RGBAFormat&&utils.convert(textureFormat)!==_gl.getParameter(35739)){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}const halfFloatSupportedByExt=textureType===HalfFloatType&&(extensions.has("EXT_color_buffer_half_float")||capabilities.isWebGL2&&extensions.has("EXT_color_buffer_float"));if(textureType!==UnsignedByteType&&utils.convert(textureType)!==_gl.getParameter(35738)&&!(textureType===FloatType&&(capabilities.isWebGL2||extensions.has("OES_texture_float")||extensions.has("WEBGL_color_buffer_float")))&&!halfFloatSupportedByExt){console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}if(x>=0&&x<=renderTarget.width-width&&(y>=0&&y<=renderTarget.height-height)){_gl.readPixels(x,y,width,height,utils.convert(textureFormat),utils.convert(textureType),buffer)}}finally{const framebuffer=_currentRenderTarget!==null?properties.get(_currentRenderTarget).__webglFramebuffer:null;state.bindFramebuffer(36160,framebuffer)}}};this.copyFramebufferToTexture=function(position,texture,level=0){if(texture.isFramebufferTexture!==true){console.error("THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture.");return}const levelScale=Math.pow(2,-level);const width=Math.floor(texture.image.width*levelScale);const height=Math.floor(texture.image.height*levelScale);textures.setTexture2D(texture,0);_gl.copyTexSubImage2D(3553,level,0,0,position.x,position.y,width,height);state.unbindTexture()};this.copyTextureToTexture=function(position,srcTexture,dstTexture,level=0){const width=srcTexture.image.width;const height=srcTexture.image.height;const glFormat=utils.convert(dstTexture.format);const glType=utils.convert(dstTexture.type);textures.setTexture2D(dstTexture,0);_gl.pixelStorei(37440,dstTexture.flipY);_gl.pixelStorei(37441,dstTexture.premultiplyAlpha);_gl.pixelStorei(3317,dstTexture.unpackAlignment);if(srcTexture.isDataTexture){_gl.texSubImage2D(3553,level,position.x,position.y,width,height,glFormat,glType,srcTexture.image.data)}else{if(srcTexture.isCompressedTexture){_gl.compressedTexSubImage2D(3553,level,position.x,position.y,srcTexture.mipmaps[0].width,srcTexture.mipmaps[0].height,glFormat,srcTexture.mipmaps[0].data)}else{_gl.texSubImage2D(3553,level,position.x,position.y,glFormat,glType,srcTexture.image)}}if(level===0&&dstTexture.generateMipmaps)_gl.generateMipmap(3553);state.unbindTexture()};this.copyTextureToTexture3D=function(sourceBox,position,srcTexture,dstTexture,level=0){if(_this.isWebGL1Renderer){console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.");return}const width=sourceBox.max.x-sourceBox.min.x+1;const height=sourceBox.max.y-sourceBox.min.y+1;const depth=sourceBox.max.z-sourceBox.min.z+1;const glFormat=utils.convert(dstTexture.format);const glType=utils.convert(dstTexture.type);let glTarget;if(dstTexture.isData3DTexture){textures.setTexture3D(dstTexture,0);glTarget=32879}else if(dstTexture.isDataArrayTexture){textures.setTexture2DArray(dstTexture,0);glTarget=35866}else{console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.");return}_gl.pixelStorei(37440,dstTexture.flipY);_gl.pixelStorei(37441,dstTexture.premultiplyAlpha);_gl.pixelStorei(3317,dstTexture.unpackAlignment);const unpackRowLen=_gl.getParameter(3314);const unpackImageHeight=_gl.getParameter(32878);const unpackSkipPixels=_gl.getParameter(3316);const unpackSkipRows=_gl.getParameter(3315);const unpackSkipImages=_gl.getParameter(32877);const image=srcTexture.isCompressedTexture?srcTexture.mipmaps[0]:srcTexture.image;_gl.pixelStorei(3314,image.width);_gl.pixelStorei(32878,image.height);_gl.pixelStorei(3316,sourceBox.min.x);_gl.pixelStorei(3315,sourceBox.min.y);_gl.pixelStorei(32877,sourceBox.min.z);if(srcTexture.isDataTexture||srcTexture.isData3DTexture){_gl.texSubImage3D(glTarget,level,position.x,position.y,position.z,width,height,depth,glFormat,glType,image.data)}else{if(srcTexture.isCompressedTexture){console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.");_gl.compressedTexSubImage3D(glTarget,level,position.x,position.y,position.z,width,height,depth,glFormat,image.data)}else{_gl.texSubImage3D(glTarget,level,position.x,position.y,position.z,width,height,depth,glFormat,glType,image)}}_gl.pixelStorei(3314,unpackRowLen);_gl.pixelStorei(32878,unpackImageHeight);_gl.pixelStorei(3316,unpackSkipPixels);_gl.pixelStorei(3315,unpackSkipRows);_gl.pixelStorei(32877,unpackSkipImages);if(level===0&&dstTexture.generateMipmaps)_gl.generateMipmap(glTarget);state.unbindTexture()};this.initTexture=function(texture){textures.setTexture2D(texture,0);state.unbindTexture()};this.resetState=function(){_currentActiveCubeFace=0;_currentActiveMipmapLevel=0;_currentRenderTarget=null;state.reset();bindingStates.reset()};if(typeof __THREE_DEVTOOLS__!=="undefined"){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}}WebGLRenderer.prototype.isWebGLRenderer=true;class WebGL1Renderer extends WebGLRenderer{}WebGL1Renderer.prototype.isWebGL1Renderer=true;class Scene extends Object3D{constructor(){super();this.type="Scene";this.background=null;this.environment=null;this.fog=null;this.overrideMaterial=null;this.autoUpdate=true;if(typeof __THREE_DEVTOOLS__!=="undefined"){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}}copy(source,recursive){super.copy(source,recursive);if(source.background!==null)this.background=source.background.clone();if(source.environment!==null)this.environment=source.environment.clone();if(source.fog!==null)this.fog=source.fog.clone();if(source.overrideMaterial!==null)this.overrideMaterial=source.overrideMaterial.clone();this.autoUpdate=source.autoUpdate;this.matrixAutoUpdate=source.matrixAutoUpdate;return this}toJSON(meta){const data=super.toJSON(meta);if(this.fog!==null)data.object.fog=this.fog.toJSON();return data}}Scene.prototype.isScene=true;class InterleavedBuffer{constructor(array,stride){this.array=array;this.stride=stride;this.count=array!==undefined?array.length/stride:0;this.usage=StaticDrawUsage;this.updateRange={offset:0,count:-1};this.version=0;this.uuid=generateUUID()}onUploadCallback(){}set needsUpdate(value){if(value===true)this.version++}setUsage(value){this.usage=value;return this}copy(source){this.array=new source.array.constructor(source.array);this.count=source.count;this.stride=source.stride;this.usage=source.usage;return this}copyAt(index1,attribute,index2){index1*=this.stride;index2*=attribute.stride;for(let i=0,l=this.stride;iraycaster.far)return;intersects.push({distance:distance,point:_intersectPoint.clone(),uv:Triangle.getUV(_intersectPoint,_vA,_vB,_vC,_uvA,_uvB,_uvC,new Vector2),face:null,object:this})}copy(source){super.copy(source);if(source.center!==undefined)this.center.copy(source.center);this.material=source.material;return this}}Sprite.prototype.isSprite=true;function transformVertex(vertexPosition,mvPosition,center,scale,sin,cos){_alignedPosition.subVectors(vertexPosition,center).addScalar(.5).multiply(scale);if(sin!==undefined){_rotatedPosition.x=cos*_alignedPosition.x-sin*_alignedPosition.y;_rotatedPosition.y=sin*_alignedPosition.x+cos*_alignedPosition.y}else{_rotatedPosition.copy(_alignedPosition)}vertexPosition.copy(mvPosition);vertexPosition.x+=_rotatedPosition.x;vertexPosition.y+=_rotatedPosition.y;vertexPosition.applyMatrix4(_viewWorldMatrix)}const _basePosition=new Vector3;const _skinIndex=new Vector4;const _skinWeight=new Vector4;const _vector$5=new Vector3;const _matrix=new Matrix4;class SkinnedMesh extends Mesh{constructor(geometry,material){super(geometry,material);this.type="SkinnedMesh";this.bindMode="attached";this.bindMatrix=new Matrix4;this.bindMatrixInverse=new Matrix4}copy(source){super.copy(source);this.bindMode=source.bindMode;this.bindMatrix.copy(source.bindMatrix);this.bindMatrixInverse.copy(source.bindMatrixInverse);this.skeleton=source.skeleton;return this}bind(skeleton,bindMatrix){this.skeleton=skeleton;if(bindMatrix===undefined){this.updateMatrixWorld(true);this.skeleton.calculateInverses();bindMatrix=this.matrixWorld}this.bindMatrix.copy(bindMatrix);this.bindMatrixInverse.copy(bindMatrix).invert()}pose(){this.skeleton.pose()}normalizeSkinWeights(){const vector=new Vector4;const skinWeight=this.geometry.attributes.skinWeight;for(let i=0,l=skinWeight.count;ilocalThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);const distance=raycaster.ray.origin.distanceTo(interRay);if(distanceraycaster.far)continue;intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}else{const start=Math.max(0,drawRange.start);const end=Math.min(positionAttribute.count,drawRange.start+drawRange.count);for(let i=start,l=end-1;ilocalThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);const distance=raycaster.ray.origin.distanceTo(interRay);if(distanceraycaster.far)continue;intersects.push({distance:distance,point:interSegment.clone().applyMatrix4(this.matrixWorld),index:i,face:null,faceIndex:null,object:this})}}}else if(geometry.isGeometry){console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}}updateMorphTargets(){const geometry=this.geometry;if(geometry.isBufferGeometry){const morphAttributes=geometry.morphAttributes;const keys=Object.keys(morphAttributes);if(keys.length>0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}}Line.prototype.isLine=true;const _start=new Vector3;const _end=new Vector3;class LineSegments extends Line{constructor(geometry,material){super(geometry,material);this.type="LineSegments"}computeLineDistances(){const geometry=this.geometry;if(geometry.isBufferGeometry){if(geometry.index===null){const positionAttribute=geometry.attributes.position;const lineDistances=[];for(let i=0,l=positionAttribute.count;i0){const morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(let m=0,ml=morphAttribute.length;m0){console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}}}Points.prototype.isPoints=true;function testPoint(point,index,localThresholdSq,matrixWorld,raycaster,intersects,object){const rayPointDistanceSq=_ray.distanceSqToPoint(point);if(rayPointDistanceSqraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint,index:index,face:null,object:object})}}class VideoTexture extends Texture{constructor(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){super(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.minFilter=minFilter!==undefined?minFilter:LinearFilter;this.magFilter=magFilter!==undefined?magFilter:LinearFilter;this.generateMipmaps=false;const scope=this;function updateVideo(){scope.needsUpdate=true;video.requestVideoFrameCallback(updateVideo)}if("requestVideoFrameCallback"in video){video.requestVideoFrameCallback(updateVideo)}}clone(){return new this.constructor(this.image).copy(this)}update(){const video=this.image;const hasVideoFrameCallback="requestVideoFrameCallback"in video;if(hasVideoFrameCallback===false&&video.readyState>=video.HAVE_CURRENT_DATA){this.needsUpdate=true}}}VideoTexture.prototype.isVideoTexture=true;class FramebufferTexture extends Texture{constructor(width,height,format){super({width:width,height:height});this.format=format;this.magFilter=NearestFilter;this.minFilter=NearestFilter;this.generateMipmaps=false;this.needsUpdate=true}}FramebufferTexture.prototype.isFramebufferTexture=true;class CompressedTexture extends Texture{constructor(mipmaps,width,height,format,type,mapping,wrapS,wrapT,magFilter,minFilter,anisotropy,encoding){super(null,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this.image={width:width,height:height};this.mipmaps=mipmaps;this.flipY=false;this.generateMipmaps=false}}CompressedTexture.prototype.isCompressedTexture=true;class CanvasTexture extends Texture{constructor(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){super(canvas,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.needsUpdate=true}}CanvasTexture.prototype.isCanvasTexture=true;class Curve{constructor(){this.type="Curve";this.arcLengthDivisions=200}getPoint(){console.warn("THREE.Curve: .getPoint() not implemented.");return null}getPointAt(u,optionalTarget){const t=this.getUtoTmapping(u);return this.getPoint(t,optionalTarget)}getPoints(divisions=5){const points=[];for(let d=0;d<=divisions;d++){points.push(this.getPoint(d/divisions))}return points}getSpacedPoints(divisions=5){const points=[];for(let d=0;d<=divisions;d++){points.push(this.getPointAt(d/divisions))}return points}getLength(){const lengths=this.getLengths();return lengths[lengths.length-1]}getLengths(divisions=this.arcLengthDivisions){if(this.cacheArcLengths&&this.cacheArcLengths.length===divisions+1&&!this.needsUpdate){return this.cacheArcLengths}this.needsUpdate=false;const cache=[];let current,last=this.getPoint(0);let sum=0;cache.push(0);for(let p=1;p<=divisions;p++){current=this.getPoint(p/divisions);sum+=current.distanceTo(last);cache.push(sum);last=current}this.cacheArcLengths=cache;return cache}updateArcLengths(){this.needsUpdate=true;this.getLengths()}getUtoTmapping(u,distance){const arcLengths=this.getLengths();let i=0;const il=arcLengths.length;let targetArcLength;if(distance){targetArcLength=distance}else{targetArcLength=u*arcLengths[il-1]}let low=0,high=il-1,comparison;while(low<=high){i=Math.floor(low+(high-low)/2);comparison=arcLengths[i]-targetArcLength;if(comparison<0){low=i+1}else if(comparison>0){high=i-1}else{high=i;break}}i=high;if(arcLengths[i]===targetArcLength){return i/(il-1)}const lengthBefore=arcLengths[i];const lengthAfter=arcLengths[i+1];const segmentLength=lengthAfter-lengthBefore;const segmentFraction=(targetArcLength-lengthBefore)/segmentLength;const t=(i+segmentFraction)/(il-1);return t}getTangent(t,optionalTarget){const delta=1e-4;let t1=t-delta;let t2=t+delta;if(t1<0)t1=0;if(t2>1)t2=1;const pt1=this.getPoint(t1);const pt2=this.getPoint(t2);const tangent=optionalTarget||(pt1.isVector2?new Vector2:new Vector3);tangent.copy(pt2).sub(pt1).normalize();return tangent}getTangentAt(u,optionalTarget){const t=this.getUtoTmapping(u);return this.getTangent(t,optionalTarget)}computeFrenetFrames(segments,closed){const normal=new Vector3;const tangents=[];const normals=[];const binormals=[];const vec=new Vector3;const mat=new Matrix4;for(let i=0;i<=segments;i++){const u=i/segments;tangents[i]=this.getTangentAt(u,new Vector3)}normals[0]=new Vector3;binormals[0]=new Vector3;let min=Number.MAX_VALUE;const tx=Math.abs(tangents[0].x);const ty=Math.abs(tangents[0].y);const tz=Math.abs(tangents[0].z);if(tx<=min){min=tx;normal.set(1,0,0)}if(ty<=min){min=ty;normal.set(0,1,0)}if(tz<=min){normal.set(0,0,1)}vec.crossVectors(tangents[0],normal).normalize();normals[0].crossVectors(tangents[0],vec);binormals[0].crossVectors(tangents[0],normals[0]);for(let i=1;i<=segments;i++){normals[i]=normals[i-1].clone();binormals[i]=binormals[i-1].clone();vec.crossVectors(tangents[i-1],tangents[i]);if(vec.length()>Number.EPSILON){vec.normalize();const theta=Math.acos(clamp(tangents[i-1].dot(tangents[i]),-1,1));normals[i].applyMatrix4(mat.makeRotationAxis(vec,theta))}binormals[i].crossVectors(tangents[i],normals[i])}if(closed===true){let theta=Math.acos(clamp(normals[0].dot(normals[segments]),-1,1));theta/=segments;if(tangents[0].dot(vec.crossVectors(normals[0],normals[segments]))>0){theta=-theta}for(let i=1;i<=segments;i++){normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i],theta*i));binormals[i].crossVectors(tangents[i],normals[i])}}return{tangents:tangents,normals:normals,binormals:binormals}}clone(){return(new this.constructor).copy(this)}copy(source){this.arcLengthDivisions=source.arcLengthDivisions;return this}toJSON(){const data={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};data.arcLengthDivisions=this.arcLengthDivisions;data.type=this.type;return data}fromJSON(json){this.arcLengthDivisions=json.arcLengthDivisions;return this}}class EllipseCurve extends Curve{constructor(aX=0,aY=0,xRadius=1,yRadius=1,aStartAngle=0,aEndAngle=Math.PI*2,aClockwise=false,aRotation=0){super();this.type="EllipseCurve";this.aX=aX;this.aY=aY;this.xRadius=xRadius;this.yRadius=yRadius;this.aStartAngle=aStartAngle;this.aEndAngle=aEndAngle;this.aClockwise=aClockwise;this.aRotation=aRotation}getPoint(t,optionalTarget){const point=optionalTarget||new Vector2;const twoPi=Math.PI*2;let deltaAngle=this.aEndAngle-this.aStartAngle;const samePoints=Math.abs(deltaAngle)twoPi)deltaAngle-=twoPi;if(deltaAngle0?0:(Math.floor(Math.abs(intPoint)/l)+1)*l}else if(weight===0&&intPoint===l-1){intPoint=l-2;weight=1}let p0,p3;if(this.closed||intPoint>0){p0=points[(intPoint-1)%l]}else{tmp.subVectors(points[0],points[1]).add(points[0]);p0=tmp}const p1=points[intPoint%l];const p2=points[(intPoint+1)%l];if(this.closed||intPoint+2points.length-2?points.length-1:intPoint+1];const p3=points[intPoint>points.length-3?points.length-1:intPoint+2];point.set(CatmullRom(weight,p0.x,p1.x,p2.x,p3.x),CatmullRom(weight,p0.y,p1.y,p2.y,p3.y));return point}copy(source){super.copy(source);this.points=[];for(let i=0,l=source.points.length;i=d){const diff=curveLengths[i]-d;const curve=this.curves[i];const segmentLength=curve.getLength();const u=segmentLength===0?0:1-diff/segmentLength;return curve.getPointAt(u,optionalTarget)}i++}return null}getLength(){const lens=this.getCurveLengths();return lens[lens.length-1]}updateArcLengths(){this.needsUpdate=true;this.cacheLengths=null;this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length){return this.cacheLengths}const lengths=[];let sums=0;for(let i=0,l=this.curves.length;i1&&!points[points.length-1].equals(points[0])){points.push(points[0])}return points}copy(source){super.copy(source);this.curves=[];for(let i=0,l=source.curves.length;i0){const firstPoint=curve.getPoint(0);if(!firstPoint.equals(this.currentPoint)){this.lineTo(firstPoint.x,firstPoint.y)}}this.curves.push(curve);const lastPoint=curve.getPoint(1);this.currentPoint.copy(lastPoint);return this}copy(source){super.copy(source);this.currentPoint.copy(source.currentPoint);return this}toJSON(){const data=super.toJSON();data.currentPoint=this.currentPoint.toArray();return data}fromJSON(json){super.fromJSON(json);this.currentPoint.fromArray(json.currentPoint);return this}}new Vector3;new Vector3;new Vector3;new Triangle;class Shape extends Path{constructor(points){super(points);this.uuid=generateUUID();this.type="Shape";this.holes=[]}getPointsHoles(divisions){const holesPts=[];for(let i=0,l=this.holes.length;i80*dim){minX=maxX=data[0];minY=maxY=data[1];for(let i=dim;imaxX)maxX=x;if(y>maxY)maxY=y}invSize=Math.max(maxX-minX,maxY-minY);invSize=invSize!==0?1/invSize:0}earcutLinked(outerNode,triangles,dim,minX,minY,invSize);return triangles}};function linkedList(data,start,end,dim,clockwise){let i,last;if(clockwise===signedArea(data,start,end,dim)>0){for(i=start;i=start;i-=dim)last=insertNode(i,data[i],data[i+1],last)}if(last&&equals(last,last.next)){removeNode(last);last=last.next}return last}function filterPoints(start,end){if(!start)return start;if(!end)end=start;let p=start,again;do{again=false;if(!p.steiner&&(equals(p,p.next)||area(p.prev,p,p.next)===0)){removeNode(p);p=end=p.prev;if(p===p.next)break;again=true}else{p=p.next}}while(again||p!==end);return end}function earcutLinked(ear,triangles,dim,minX,minY,invSize,pass){if(!ear)return;if(!pass&&invSize)indexCurve(ear,minX,minY,invSize);let stop=ear,prev,next;while(ear.prev!==ear.next){prev=ear.prev;next=ear.next;if(invSize?isEarHashed(ear,minX,minY,invSize):isEar(ear)){triangles.push(prev.i/dim);triangles.push(ear.i/dim);triangles.push(next.i/dim);removeNode(ear);ear=next.next;stop=next.next;continue}ear=next;if(ear===stop){if(!pass){earcutLinked(filterPoints(ear),triangles,dim,minX,minY,invSize,1)}else if(pass===1){ear=cureLocalIntersections(filterPoints(ear),triangles,dim);earcutLinked(ear,triangles,dim,minX,minY,invSize,2)}else if(pass===2){splitEarcut(ear,triangles,dim,minX,minY,invSize)}break}}}function isEar(ear){const a=ear.prev,b=ear,c=ear.next;if(area(a,b,c)>=0)return false;let p=ear.next.next;while(p!==ear.prev){if(pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.next}return true}function isEarHashed(ear,minX,minY,invSize){const a=ear.prev,b=ear,c=ear.next;if(area(a,b,c)>=0)return false;const minTX=a.xb.x?a.x>c.x?a.x:c.x:b.x>c.x?b.x:c.x,maxTY=a.y>b.y?a.y>c.y?a.y:c.y:b.y>c.y?b.y:c.y;const minZ=zOrder(minTX,minTY,minX,minY,invSize),maxZ=zOrder(maxTX,maxTY,minX,minY,invSize);let p=ear.prevZ,n=ear.nextZ;while(p&&p.z>=minZ&&n&&n.z<=maxZ){if(p!==ear.prev&&p!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.prevZ;if(n!==ear.prev&&n!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,n.x,n.y)&&area(n.prev,n,n.next)>=0)return false;n=n.nextZ}while(p&&p.z>=minZ){if(p!==ear.prev&&p!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,p.x,p.y)&&area(p.prev,p,p.next)>=0)return false;p=p.prevZ}while(n&&n.z<=maxZ){if(n!==ear.prev&&n!==ear.next&&pointInTriangle(a.x,a.y,b.x,b.y,c.x,c.y,n.x,n.y)&&area(n.prev,n,n.next)>=0)return false;n=n.nextZ}return true}function cureLocalIntersections(start,triangles,dim){let p=start;do{const a=p.prev,b=p.next.next;if(!equals(a,b)&&intersects(a,p,p.next,b)&&locallyInside(a,b)&&locallyInside(b,a)){triangles.push(a.i/dim);triangles.push(p.i/dim);triangles.push(b.i/dim);removeNode(p);removeNode(p.next);p=start=b}p=p.next}while(p!==start);return filterPoints(p)}function splitEarcut(start,triangles,dim,minX,minY,invSize){let a=start;do{let b=a.next.next;while(b!==a.prev){if(a.i!==b.i&&isValidDiagonal(a,b)){let c=splitPolygon(a,b);a=filterPoints(a,a.next);c=filterPoints(c,c.next);earcutLinked(a,triangles,dim,minX,minY,invSize);earcutLinked(c,triangles,dim,minX,minY,invSize);return}b=b.next}a=a.next}while(a!==start)}function eliminateHoles(data,holeIndices,outerNode,dim){const queue=[];let i,len,start,end,list;for(i=0,len=holeIndices.length;i=p.next.y&&p.next.y!==p.y){const x=p.x+(hy-p.y)*(p.next.x-p.x)/(p.next.y-p.y);if(x<=hx&&x>qx){qx=x;if(x===hx){if(hy===p.y)return p;if(hy===p.next.y)return p.next}m=p.x=p.x&&p.x>=mx&&hx!==p.x&&pointInTriangle(hym.x||p.x===m.x&§orContainsSector(m,p)))){m=p;tanMin=tan}}p=p.next}while(p!==stop);return m}function sectorContainsSector(m,p){return area(m.prev,m,p.prev)<0&&area(p.next,m,m.next)<0}function indexCurve(start,minX,minY,invSize){let p=start;do{if(p.z===null)p.z=zOrder(p.x,p.y,minX,minY,invSize);p.prevZ=p.prev;p.nextZ=p.next;p=p.next}while(p!==start);p.prevZ.nextZ=null;p.prevZ=null;sortLinked(p)}function sortLinked(list){let i,p,q,e,tail,numMerges,pSize,qSize,inSize=1;do{p=list;list=null;tail=null;numMerges=0;while(p){numMerges++;q=p;pSize=0;for(i=0;i0||qSize>0&&q){if(pSize!==0&&(qSize===0||!q||p.z<=q.z)){e=p;p=p.nextZ;pSize--}else{e=q;q=q.nextZ;qSize--}if(tail)tail.nextZ=e;else list=e;e.prevZ=tail;tail=e}p=q}tail.nextZ=null;inSize*=2}while(numMerges>1);return list}function zOrder(x,y,minX,minY,invSize){x=32767*(x-minX)*invSize;y=32767*(y-minY)*invSize;x=(x|x<<8)&16711935;x=(x|x<<4)&252645135;x=(x|x<<2)&858993459;x=(x|x<<1)&1431655765;y=(y|y<<8)&16711935;y=(y|y<<4)&252645135;y=(y|y<<2)&858993459;y=(y|y<<1)&1431655765;return x|y<<1}function getLeftmost(start){let p=start,leftmost=start;do{if(p.x=0&&(ax-px)*(by-py)-(bx-px)*(ay-py)>=0&&(bx-px)*(cy-py)-(cx-px)*(by-py)>=0}function isValidDiagonal(a,b){return a.next.i!==b.i&&a.prev.i!==b.i&&!intersectsPolygon(a,b)&&(locallyInside(a,b)&&locallyInside(b,a)&&middleInside(a,b)&&(area(a.prev,a,b.prev)||area(a,b.prev,b))||equals(a,b)&&area(a.prev,a,a.next)>0&&area(b.prev,b,b.next)>0)}function area(p,q,r){return(q.y-p.y)*(r.x-q.x)-(q.x-p.x)*(r.y-q.y)}function equals(p1,p2){return p1.x===p2.x&&p1.y===p2.y}function intersects(p1,q1,p2,q2){const o1=sign(area(p1,q1,p2));const o2=sign(area(p1,q1,q2));const o3=sign(area(p2,q2,p1));const o4=sign(area(p2,q2,q1));if(o1!==o2&&o3!==o4)return true;if(o1===0&&onSegment(p1,p2,q1))return true;if(o2===0&&onSegment(p1,q2,q1))return true;if(o3===0&&onSegment(p2,p1,q2))return true;if(o4===0&&onSegment(p2,q1,q2))return true;return false}function onSegment(p,q,r){return q.x<=Math.max(p.x,r.x)&&q.x>=Math.min(p.x,r.x)&&q.y<=Math.max(p.y,r.y)&&q.y>=Math.min(p.y,r.y)}function sign(num){return num>0?1:num<0?-1:0}function intersectsPolygon(a,b){let p=a;do{if(p.i!==a.i&&p.next.i!==a.i&&p.i!==b.i&&p.next.i!==b.i&&intersects(p,p.next,a,b))return true;p=p.next}while(p!==a);return false}function locallyInside(a,b){return area(a.prev,a,a.next)<0?area(a,b,a.next)>=0&&area(a,a.prev,b)>=0:area(a,b,a.prev)<0||area(a,a.next,b)<0}function middleInside(a,b){let p=a,inside=false;const px=(a.x+b.x)/2,py=(a.y+b.y)/2;do{if(p.y>py!==p.next.y>py&&p.next.y!==p.y&&px<(p.next.x-p.x)*(py-p.y)/(p.next.y-p.y)+p.x)inside=!inside;p=p.next}while(p!==a);return inside}function splitPolygon(a,b){const a2=new Node(a.i,a.x,a.y),b2=new Node(b.i,b.x,b.y),an=a.next,bp=b.prev;a.next=b;b.prev=a;a2.next=an;an.prev=a2;b2.next=a2;a2.prev=b2;bp.next=b2;b2.prev=bp;return b2}function insertNode(i,x,y,last){const p=new Node(i,x,y);if(!last){p.prev=p;p.next=p}else{p.next=last.next;p.prev=last;last.next.prev=p;last.next=p}return p}function removeNode(p){p.next.prev=p.prev;p.prev.next=p.next;if(p.prevZ)p.prevZ.nextZ=p.nextZ;if(p.nextZ)p.nextZ.prevZ=p.prevZ}function Node(i,x,y){this.i=i;this.x=x;this.y=y;this.prev=null;this.next=null;this.z=null;this.prevZ=null;this.nextZ=null;this.steiner=false}function signedArea(data,start,end,dim){let sum=0;for(let i=start,j=end-dim;i2&&points[l-1].equals(points[0])){points.pop()}}function addContour(vertices,contour){for(let i=0;iNumber.EPSILON){const v_prev_len=Math.sqrt(v_prev_lensq);const v_next_len=Math.sqrt(v_next_x*v_next_x+v_next_y*v_next_y);const ptPrevShift_x=inPrev.x-v_prev_y/v_prev_len;const ptPrevShift_y=inPrev.y+v_prev_x/v_prev_len;const ptNextShift_x=inNext.x-v_next_y/v_next_len;const ptNextShift_y=inNext.y+v_next_x/v_next_len;const sf=((ptNextShift_x-ptPrevShift_x)*v_next_y-(ptNextShift_y-ptPrevShift_y)*v_next_x)/(v_prev_x*v_next_y-v_prev_y*v_next_x);v_trans_x=ptPrevShift_x+v_prev_x*sf-inPt.x;v_trans_y=ptPrevShift_y+v_prev_y*sf-inPt.y;const v_trans_lensq=v_trans_x*v_trans_x+v_trans_y*v_trans_y;if(v_trans_lensq<=2){return new Vector2(v_trans_x,v_trans_y)}else{shrink_by=Math.sqrt(v_trans_lensq/2)}}else{let direction_eq=false;if(v_prev_x>Number.EPSILON){if(v_next_x>Number.EPSILON){direction_eq=true}}else{if(v_prev_x<-Number.EPSILON){if(v_next_x<-Number.EPSILON){direction_eq=true}}else{if(Math.sign(v_prev_y)===Math.sign(v_next_y)){direction_eq=true}}}if(direction_eq){v_trans_x=-v_prev_y;v_trans_y=v_prev_x;shrink_by=Math.sqrt(v_prev_lensq)}else{v_trans_x=v_prev_x;v_trans_y=v_prev_y;shrink_by=Math.sqrt(v_prev_lensq/2)}}return new Vector2(v_trans_x/shrink_by,v_trans_y/shrink_by)}const contourMovements=[];for(let i=0,il=contour.length,j=il-1,k=i+1;i=0;b--){const t=b/bevelSegments;const z=bevelThickness*Math.cos(t*Math.PI/2);const bs=bevelSize*Math.sin(t*Math.PI/2)+bevelOffset;for(let i=0,il=contour.length;i=0){const j=i;let k=i-1;if(k<0)k=contour.length-1;for(let s=0,sl=steps+bevelSegments*2;s0!==value>0){this.version++}this._sheen=value}get clearcoat(){return this._clearcoat}set clearcoat(value){if(this._clearcoat>0!==value>0){this.version++}this._clearcoat=value}get transmission(){return this._transmission}set transmission(value){if(this._transmission>0!==value>0){this.version++}this._transmission=value}copy(source){super.copy(source);this.defines={STANDARD:"",PHYSICAL:""};this.clearcoat=source.clearcoat;this.clearcoatMap=source.clearcoatMap;this.clearcoatRoughness=source.clearcoatRoughness;this.clearcoatRoughnessMap=source.clearcoatRoughnessMap;this.clearcoatNormalMap=source.clearcoatNormalMap;this.clearcoatNormalScale.copy(source.clearcoatNormalScale);this.ior=source.ior;this.sheen=source.sheen;this.sheenColor.copy(source.sheenColor);this.sheenColorMap=source.sheenColorMap;this.sheenRoughness=source.sheenRoughness;this.sheenRoughnessMap=source.sheenRoughnessMap;this.transmission=source.transmission;this.transmissionMap=source.transmissionMap;this.thickness=source.thickness;this.thicknessMap=source.thicknessMap;this.attenuationDistance=source.attenuationDistance;this.attenuationColor.copy(source.attenuationColor);this.specularIntensity=source.specularIntensity;this.specularIntensityMap=source.specularIntensityMap;this.specularColor.copy(source.specularColor);this.specularColorMap=source.specularColorMap;return this}}MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial=true;class MeshPhongMaterial extends Material{constructor(parameters){super();this.type="MeshPhongMaterial";this.color=new Color(16777215);this.specular=new Color(1118481);this.shininess=30;this.map=null;this.lightMap=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new Color(0);this.emissiveIntensity=1;this.emissiveMap=null;this.bumpMap=null;this.bumpScale=1;this.normalMap=null;this.normalMapType=TangentSpaceNormalMap;this.normalScale=new Vector2(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.specularMap=null;this.alphaMap=null;this.envMap=null;this.combine=MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.wireframe=false;this.wireframeLinewidth=1;this.wireframeLinecap="round";this.wireframeLinejoin="round";this.flatShading=false;this.setValues(parameters)}copy(source){super.copy(source);this.color.copy(source.color);this.specular.copy(source.specular);this.shininess=source.shininess;this.map=source.map;this.lightMap=source.lightMap;this.lightMapIntensity=source.lightMapIntensity;this.aoMap=source.aoMap;this.aoMapIntensity=source.aoMapIntensity;this.emissive.copy(source.emissive);this.emissiveMap=source.emissiveMap;this.emissiveIntensity=source.emissiveIntensity;this.bumpMap=source.bumpMap;this.bumpScale=source.bumpScale;this.normalMap=source.normalMap;this.normalMapType=source.normalMapType;this.normalScale.copy(source.normalScale);this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.specularMap=source.specularMap;this.alphaMap=source.alphaMap;this.envMap=source.envMap;this.combine=source.combine;this.reflectivity=source.reflectivity;this.refractionRatio=source.refractionRatio;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;this.wireframeLinecap=source.wireframeLinecap;this.wireframeLinejoin=source.wireframeLinejoin;this.flatShading=source.flatShading;return this}}MeshPhongMaterial.prototype.isMeshPhongMaterial=true;class MeshToonMaterial extends Material{constructor(parameters){super();this.defines={TOON:""};this.type="MeshToonMaterial";this.color=new Color(16777215);this.map=null;this.gradientMap=null;this.lightMap=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new Color(0);this.emissiveIntensity=1;this.emissiveMap=null;this.bumpMap=null;this.bumpScale=1;this.normalMap=null;this.normalMapType=TangentSpaceNormalMap;this.normalScale=new Vector2(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.alphaMap=null;this.wireframe=false;this.wireframeLinewidth=1;this.wireframeLinecap="round";this.wireframeLinejoin="round";this.setValues(parameters)}copy(source){super.copy(source);this.color.copy(source.color);this.map=source.map;this.gradientMap=source.gradientMap;this.lightMap=source.lightMap;this.lightMapIntensity=source.lightMapIntensity;this.aoMap=source.aoMap;this.aoMapIntensity=source.aoMapIntensity;this.emissive.copy(source.emissive);this.emissiveMap=source.emissiveMap;this.emissiveIntensity=source.emissiveIntensity;this.bumpMap=source.bumpMap;this.bumpScale=source.bumpScale;this.normalMap=source.normalMap;this.normalMapType=source.normalMapType;this.normalScale.copy(source.normalScale);this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.alphaMap=source.alphaMap;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;this.wireframeLinecap=source.wireframeLinecap;this.wireframeLinejoin=source.wireframeLinejoin;return this}}MeshToonMaterial.prototype.isMeshToonMaterial=true;class MeshNormalMaterial extends Material{constructor(parameters){super();this.type="MeshNormalMaterial";this.bumpMap=null;this.bumpScale=1;this.normalMap=null;this.normalMapType=TangentSpaceNormalMap;this.normalScale=new Vector2(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.wireframe=false;this.wireframeLinewidth=1;this.fog=false;this.flatShading=false;this.setValues(parameters)}copy(source){super.copy(source);this.bumpMap=source.bumpMap;this.bumpScale=source.bumpScale;this.normalMap=source.normalMap;this.normalMapType=source.normalMapType;this.normalScale.copy(source.normalScale);this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;this.flatShading=source.flatShading;return this}}MeshNormalMaterial.prototype.isMeshNormalMaterial=true;class MeshLambertMaterial extends Material{constructor(parameters){super();this.type="MeshLambertMaterial";this.color=new Color(16777215);this.map=null;this.lightMap=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new Color(0);this.emissiveIntensity=1;this.emissiveMap=null;this.specularMap=null;this.alphaMap=null;this.envMap=null;this.combine=MultiplyOperation;this.reflectivity=1;this.refractionRatio=.98;this.wireframe=false;this.wireframeLinewidth=1;this.wireframeLinecap="round";this.wireframeLinejoin="round";this.setValues(parameters)}copy(source){super.copy(source);this.color.copy(source.color);this.map=source.map;this.lightMap=source.lightMap;this.lightMapIntensity=source.lightMapIntensity;this.aoMap=source.aoMap;this.aoMapIntensity=source.aoMapIntensity;this.emissive.copy(source.emissive);this.emissiveMap=source.emissiveMap;this.emissiveIntensity=source.emissiveIntensity;this.specularMap=source.specularMap;this.alphaMap=source.alphaMap;this.envMap=source.envMap;this.combine=source.combine;this.reflectivity=source.reflectivity;this.refractionRatio=source.refractionRatio;this.wireframe=source.wireframe;this.wireframeLinewidth=source.wireframeLinewidth;this.wireframeLinecap=source.wireframeLinecap;this.wireframeLinejoin=source.wireframeLinejoin;return this}}MeshLambertMaterial.prototype.isMeshLambertMaterial=true;class MeshMatcapMaterial extends Material{constructor(parameters){super();this.defines={MATCAP:""};this.type="MeshMatcapMaterial";this.color=new Color(16777215);this.matcap=null;this.map=null;this.bumpMap=null;this.bumpScale=1;this.normalMap=null;this.normalMapType=TangentSpaceNormalMap;this.normalScale=new Vector2(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.alphaMap=null;this.flatShading=false;this.setValues(parameters)}copy(source){super.copy(source);this.defines={MATCAP:""};this.color.copy(source.color);this.matcap=source.matcap;this.map=source.map;this.bumpMap=source.bumpMap;this.bumpScale=source.bumpScale;this.normalMap=source.normalMap;this.normalMapType=source.normalMapType;this.normalScale.copy(source.normalScale);this.displacementMap=source.displacementMap;this.displacementScale=source.displacementScale;this.displacementBias=source.displacementBias;this.alphaMap=source.alphaMap;this.flatShading=source.flatShading;return this}}MeshMatcapMaterial.prototype.isMeshMatcapMaterial=true;class LineDashedMaterial extends LineBasicMaterial{constructor(parameters){super();this.type="LineDashedMaterial";this.scale=1;this.dashSize=3;this.gapSize=1;this.setValues(parameters)}copy(source){super.copy(source);this.scale=source.scale;this.dashSize=source.dashSize;this.gapSize=source.gapSize;return this}}LineDashedMaterial.prototype.isLineDashedMaterial=true;const materialLib={ShadowMaterial:ShadowMaterial,SpriteMaterial:SpriteMaterial,RawShaderMaterial:RawShaderMaterial,ShaderMaterial:ShaderMaterial,PointsMaterial:PointsMaterial,MeshPhysicalMaterial:MeshPhysicalMaterial,MeshStandardMaterial:MeshStandardMaterial,MeshPhongMaterial:MeshPhongMaterial,MeshToonMaterial:MeshToonMaterial,MeshNormalMaterial:MeshNormalMaterial,MeshLambertMaterial:MeshLambertMaterial,MeshDepthMaterial:MeshDepthMaterial,MeshDistanceMaterial:MeshDistanceMaterial,MeshBasicMaterial:MeshBasicMaterial,MeshMatcapMaterial:MeshMatcapMaterial,LineDashedMaterial:LineDashedMaterial,LineBasicMaterial:LineBasicMaterial,Material:Material};Material.fromType=function(type){return new materialLib[type]};const AnimationUtils={arraySlice:function(array,from,to){if(AnimationUtils.isTypedArray(array)){return new array.constructor(array.subarray(from,to!==undefined?to:array.length))}return array.slice(from,to)},convertArray:function(array,type,forceClone){if(!array||!forceClone&&array.constructor===type)return array;if(typeof type.BYTES_PER_ELEMENT==="number"){return new type(array)}return Array.prototype.slice.call(array)},isTypedArray:function(object){return ArrayBuffer.isView(object)&&!(object instanceof DataView)},getKeyframeOrder:function(times){function compareTime(i,j){return times[i]-times[j]}const n=times.length;const result=new Array(n);for(let i=0;i!==n;++i)result[i]=i;result.sort(compareTime);return result},sortedArray:function(values,stride,order){const nValues=values.length;const result=new values.constructor(nValues);for(let i=0,dstOffset=0;dstOffset!==nValues;++i){const srcOffset=order[i]*stride;for(let j=0;j!==stride;++j){result[dstOffset++]=values[srcOffset+j]}}return result},flattenJSON:function(jsonKeys,times,values,valuePropertyName){let i=1,key=jsonKeys[0];while(key!==undefined&&key[valuePropertyName]===undefined){key=jsonKeys[i++]}if(key===undefined)return;let value=key[valuePropertyName];if(value===undefined)return;if(Array.isArray(value)){do{value=key[valuePropertyName];if(value!==undefined){times.push(key.time);values.push.apply(values,value)}key=jsonKeys[i++]}while(key!==undefined)}else if(value.toArray!==undefined){do{value=key[valuePropertyName];if(value!==undefined){times.push(key.time);value.toArray(values,values.length)}key=jsonKeys[i++]}while(key!==undefined)}else{do{value=key[valuePropertyName];if(value!==undefined){times.push(key.time);values.push(value)}key=jsonKeys[i++]}while(key!==undefined)}},subclip:function(sourceClip,name,startFrame,endFrame,fps=30){const clip=sourceClip.clone();clip.name=name;const tracks=[];for(let i=0;i=endFrame)continue;times.push(track.times[j]);for(let k=0;kclip.tracks[i].times[0]){minStartTime=clip.tracks[i].times[0]}}for(let i=0;i=referenceTrack.times[lastIndex]){const startIndex=lastIndex*referenceValueSize+referenceOffset;const endIndex=startIndex+referenceValueSize-referenceOffset;referenceValue=AnimationUtils.arraySlice(referenceTrack.values,startIndex,endIndex)}else{const interpolant=referenceTrack.createInterpolant();const startIndex=referenceOffset;const endIndex=referenceValueSize-referenceOffset;interpolant.evaluate(referenceTime);referenceValue=AnimationUtils.arraySlice(interpolant.resultBuffer,startIndex,endIndex)}if(referenceTrackType==="quaternion"){const referenceQuat=(new Quaternion).fromArray(referenceValue).normalize().conjugate();referenceQuat.toArray(referenceValue)}const numTimes=targetTrack.times.length;for(let j=0;j=t0)){const t1global=pp[1];if(t=t0){break seek}}right=i1;i1=0;break linear_scan}break validate_interval}while(i1>>1;if(tendTime){--to}++to;if(from!==0||to!==nKeys){if(from>=to){to=Math.max(to,1);from=to-1}const stride=this.getValueSize();this.times=AnimationUtils.arraySlice(times,from,to);this.values=AnimationUtils.arraySlice(this.values,from*stride,to*stride)}return this}validate(){let valid=true;const valueSize=this.getValueSize();if(valueSize-Math.floor(valueSize)!==0){console.error("THREE.KeyframeTrack: Invalid value size in track.",this);valid=false}const times=this.times,values=this.values,nKeys=times.length;if(nKeys===0){console.error("THREE.KeyframeTrack: Track is empty.",this);valid=false}let prevTime=null;for(let i=0;i!==nKeys;i++){const currTime=times[i];if(typeof currTime==="number"&&isNaN(currTime)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,i,currTime);valid=false;break}if(prevTime!==null&&prevTime>currTime){console.error("THREE.KeyframeTrack: Out of order keys.",this,i,currTime,prevTime);valid=false;break}prevTime=currTime}if(values!==undefined){if(AnimationUtils.isTypedArray(values)){for(let i=0,n=values.length;i!==n;++i){const value=values[i];if(isNaN(value)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,i,value);valid=false;break}}}}return valid}optimize(){const times=AnimationUtils.arraySlice(this.times),values=AnimationUtils.arraySlice(this.values),stride=this.getValueSize(),smoothInterpolation=this.getInterpolation()===InterpolateSmooth,lastIndex=times.length-1;let writeIndex=1;for(let i=1;i0){times[writeIndex]=times[lastIndex];for(let readOffset=lastIndex*stride,writeOffset=writeIndex*stride,j=0;j!==stride;++j){values[writeOffset+j]=values[readOffset+j]}++writeIndex}if(writeIndex!==times.length){this.times=AnimationUtils.arraySlice(times,0,writeIndex);this.values=AnimationUtils.arraySlice(values,0,writeIndex*stride)}else{this.times=times;this.values=values}return this}clone(){const times=AnimationUtils.arraySlice(this.times,0);const values=AnimationUtils.arraySlice(this.values,0);const TypedKeyframeTrack=this.constructor;const track=new TypedKeyframeTrack(this.name,times,values);track.createInterpolant=this.createInterpolant;return track}}KeyframeTrack.prototype.TimeBufferType=Float32Array;KeyframeTrack.prototype.ValueBufferType=Float32Array;KeyframeTrack.prototype.DefaultInterpolation=InterpolateLinear;class BooleanKeyframeTrack extends KeyframeTrack{}BooleanKeyframeTrack.prototype.ValueTypeName="bool";BooleanKeyframeTrack.prototype.ValueBufferType=Array;BooleanKeyframeTrack.prototype.DefaultInterpolation=InterpolateDiscrete;BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear=undefined;BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class ColorKeyframeTrack extends KeyframeTrack{}ColorKeyframeTrack.prototype.ValueTypeName="color";class NumberKeyframeTrack extends KeyframeTrack{}NumberKeyframeTrack.prototype.ValueTypeName="number";class QuaternionLinearInterpolant extends Interpolant{constructor(parameterPositions,sampleValues,sampleSize,resultBuffer){super(parameterPositions,sampleValues,sampleSize,resultBuffer)}interpolate_(i1,t0,t,t1){const result=this.resultBuffer,values=this.sampleValues,stride=this.valueSize,alpha=(t-t0)/(t1-t0);let offset=i1*stride;for(let end=offset+stride;offset!==end;offset+=4){Quaternion.slerpFlat(result,0,values,offset-stride,values,offset,alpha)}return result}}class QuaternionKeyframeTrack extends KeyframeTrack{InterpolantFactoryMethodLinear(result){return new QuaternionLinearInterpolant(this.times,this.values,this.getValueSize(),result)}}QuaternionKeyframeTrack.prototype.ValueTypeName="quaternion";QuaternionKeyframeTrack.prototype.DefaultInterpolation=InterpolateLinear;QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class StringKeyframeTrack extends KeyframeTrack{}StringKeyframeTrack.prototype.ValueTypeName="string";StringKeyframeTrack.prototype.ValueBufferType=Array;StringKeyframeTrack.prototype.DefaultInterpolation=InterpolateDiscrete;StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear=undefined;StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth=undefined;class VectorKeyframeTrack extends KeyframeTrack{}VectorKeyframeTrack.prototype.ValueTypeName="vector";class AnimationClip{constructor(name,duration=-1,tracks,blendMode=NormalAnimationBlendMode){this.name=name;this.tracks=tracks;this.duration=duration;this.blendMode=blendMode;this.uuid=generateUUID();if(this.duration<0){this.resetDuration()}}static parse(json){const tracks=[],jsonTracks=json.tracks,frameTime=1/(json.fps||1);for(let i=0,n=jsonTracks.length;i!==n;++i){tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime))}const clip=new this(json.name,json.duration,tracks,json.blendMode);clip.uuid=json.uuid;return clip}static toJSON(clip){const tracks=[],clipTracks=clip.tracks;const json={name:clip.name,duration:clip.duration,tracks:tracks,uuid:clip.uuid,blendMode:clip.blendMode};for(let i=0,n=clipTracks.length;i!==n;++i){tracks.push(KeyframeTrack.toJSON(clipTracks[i]))}return json}static CreateFromMorphTargetSequence(name,morphTargetSequence,fps,noLoop){const numMorphTargets=morphTargetSequence.length;const tracks=[];for(let i=0;i1){const name=parts[1];let animationMorphTargets=animationToMorphTargets[name];if(!animationMorphTargets){animationToMorphTargets[name]=animationMorphTargets=[]}animationMorphTargets.push(morphTarget)}}const clips=[];for(const name in animationToMorphTargets){clips.push(this.CreateFromMorphTargetSequence(name,animationToMorphTargets[name],fps,noLoop))}return clips}static parseAnimation(animation,bones){if(!animation){console.error("THREE.AnimationClip: No animation in JSONLoader data.");return null}const addNonemptyTrack=function(trackType,trackName,animationKeys,propertyName,destTracks){if(animationKeys.length!==0){const times=[];const values=[];AnimationUtils.flattenJSON(animationKeys,times,values,propertyName);if(times.length!==0){destTracks.push(new trackType(trackName,times,values))}}};const tracks=[];const clipName=animation.name||"default";const fps=animation.fps||30;const blendMode=animation.blendMode;let duration=animation.length||-1;const hierarchyTracks=animation.hierarchy||[];for(let h=0;h{if(onLoad)onLoad(cached);this.manager.itemEnd(url)}),0);return cached}if(loading[url]!==undefined){loading[url].push({onLoad:onLoad,onProgress:onProgress,onError:onError});return}loading[url]=[];loading[url].push({onLoad:onLoad,onProgress:onProgress,onError:onError});const req=new Request(url,{headers:new Headers(this.requestHeader),credentials:this.withCredentials?"include":"same-origin"});const mimeType=this.mimeType;const responseType=this.responseType;fetch(req).then((response=>{if(response.status===200||response.status===0){if(response.status===0){console.warn("THREE.FileLoader: HTTP Status 0 received.")}if(typeof ReadableStream==="undefined"||response.body===undefined||response.body.getReader===undefined){return response}const callbacks=loading[url];const reader=response.body.getReader();const contentLength=response.headers.get("Content-Length");const total=contentLength?parseInt(contentLength):0;const lengthComputable=total!==0;let loaded=0;const stream=new ReadableStream({start(controller){readData();function readData(){reader.read().then((({done:done,value:value})=>{if(done){controller.close()}else{loaded+=value.byteLength;const event=new ProgressEvent("progress",{lengthComputable:lengthComputable,loaded:loaded,total:total});for(let i=0,il=callbacks.length;i{switch(responseType){case"arraybuffer":return response.arrayBuffer();case"blob":return response.blob();case"document":return response.text().then((text=>{const parser=new DOMParser;return parser.parseFromString(text,mimeType)}));case"json":return response.json();default:if(mimeType===undefined){return response.text()}else{const re=/charset="?([^;"\s]*)"?/i;const exec=re.exec(mimeType);const label=exec&&exec[1]?exec[1].toLowerCase():undefined;const decoder=new TextDecoder(label);return response.arrayBuffer().then((ab=>decoder.decode(ab)))}}})).then((data=>{Cache.add(url,data);const callbacks=loading[url];delete loading[url];for(let i=0,il=callbacks.length;i{const callbacks=loading[url];if(callbacks===undefined){this.manager.itemError(url);throw err}delete loading[url];for(let i=0,il=callbacks.length;i{this.manager.itemEnd(url)}));this.manager.itemStart(url)}setResponseType(value){this.responseType=value;return this}setMimeType(value){this.mimeType=value;return this}}class ImageLoader extends Loader{constructor(manager){super(manager)}load(url,onLoad,onProgress,onError){if(this.path!==undefined)url=this.path+url;url=this.manager.resolveURL(url);const scope=this;const cached=Cache.get(url);if(cached!==undefined){scope.manager.itemStart(url);setTimeout((function(){if(onLoad)onLoad(cached);scope.manager.itemEnd(url)}),0);return cached}const image=createElementNS("img");function onImageLoad(){removeEventListeners();Cache.add(url,this);if(onLoad)onLoad(this);scope.manager.itemEnd(url)}function onImageError(event){removeEventListeners();if(onError)onError(event);scope.manager.itemError(url);scope.manager.itemEnd(url)}function removeEventListeners(){image.removeEventListener("load",onImageLoad,false);image.removeEventListener("error",onImageError,false)}image.addEventListener("load",onImageLoad,false);image.addEventListener("error",onImageError,false);if(url.slice(0,5)!=="data:"){if(this.crossOrigin!==undefined)image.crossOrigin=this.crossOrigin}scope.manager.itemStart(url);image.src=url;return image}}class CubeTextureLoader extends Loader{constructor(manager){super(manager)}load(urls,onLoad,onProgress,onError){const texture=new CubeTexture;const loader=new ImageLoader(this.manager);loader.setCrossOrigin(this.crossOrigin);loader.setPath(this.path);let loaded=0;function loadTexture(i){loader.load(urls[i],(function(image){texture.images[i]=image;loaded++;if(loaded===6){texture.needsUpdate=true;if(onLoad)onLoad(texture)}}),undefined,onError)}for(let i=0;i0){this.source.connect(this.filters[0]);for(let i=1,l=this.filters.length;i0){this.source.disconnect(this.filters[0]);for(let i=1,l=this.filters.length;i0){this._mixBufferRegionAdditive(buffer,offset,this._addIndex*stride,1,stride)}for(let i=stride,e=stride+stride;i!==e;++i){if(buffer[i]!==buffer[i+stride]){binding.setValue(buffer,offset);break}}}saveOriginalState(){const binding=this.binding;const buffer=this.buffer,stride=this.valueSize,originalValueOffset=stride*this._origIndex;binding.getValue(buffer,originalValueOffset);for(let i=stride,e=originalValueOffset;i!==e;++i){buffer[i]=buffer[originalValueOffset+i%stride]}this._setIdentity();this.cumulativeWeight=0;this.cumulativeWeightAdditive=0}restoreOriginalState(){const originalValueOffset=this.valueSize*3;this.binding.setValue(this.buffer,originalValueOffset)}_setAdditiveIdentityNumeric(){const startIndex=this._addIndex*this.valueSize;const endIndex=startIndex+this.valueSize;for(let i=startIndex;i=.5){for(let i=0;i!==stride;++i){buffer[dstOffset+i]=buffer[srcOffset+i]}}}_slerp(buffer,dstOffset,srcOffset,t){Quaternion.slerpFlat(buffer,dstOffset,buffer,dstOffset,buffer,srcOffset,t)}_slerpAdditive(buffer,dstOffset,srcOffset,t,stride){const workOffset=this._workIndex*stride;Quaternion.multiplyQuaternionsFlat(buffer,workOffset,buffer,dstOffset,buffer,srcOffset);Quaternion.slerpFlat(buffer,dstOffset,buffer,dstOffset,buffer,workOffset,t)}_lerp(buffer,dstOffset,srcOffset,t,stride){const s=1-t;for(let i=0;i!==stride;++i){const j=dstOffset+i;buffer[j]=buffer[j]*s+buffer[srcOffset+i]*t}}_lerpAdditive(buffer,dstOffset,srcOffset,t,stride){for(let i=0;i!==stride;++i){const j=dstOffset+i;buffer[j]=buffer[j]+buffer[srcOffset+i]*t}}}const _RESERVED_CHARS_RE="\\[\\]\\.:\\/";const _reservedRe=new RegExp("["+_RESERVED_CHARS_RE+"]","g");const _wordChar="[^"+_RESERVED_CHARS_RE+"]";const _wordCharOrDot="[^"+_RESERVED_CHARS_RE.replace("\\.","")+"]";const _directoryRe=/((?:WC+[\/:])*)/.source.replace("WC",_wordChar);const _nodeRe=/(WCOD+)?/.source.replace("WCOD",_wordCharOrDot);const _objectRe=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",_wordChar);const _propertyRe=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",_wordChar);const _trackRe=new RegExp(""+"^"+_directoryRe+_nodeRe+_objectRe+_propertyRe+"$");const _supportedObjectNames=["material","materials","bones"];class Composite{constructor(targetGroup,path,optionalParsedPath){const parsedPath=optionalParsedPath||PropertyBinding.parseTrackName(path);this._targetGroup=targetGroup;this._bindings=targetGroup.subscribe_(path,parsedPath)}getValue(array,offset){this.bind();const firstValidIndex=this._targetGroup.nCachedObjects_,binding=this._bindings[firstValidIndex];if(binding!==undefined)binding.getValue(array,offset)}setValue(array,offset){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].setValue(array,offset)}}bind(){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].bind()}}unbind(){const bindings=this._bindings;for(let i=this._targetGroup.nCachedObjects_,n=bindings.length;i!==n;++i){bindings[i].unbind()}}}class PropertyBinding{constructor(rootNode,path,parsedPath){this.path=path;this.parsedPath=parsedPath||PropertyBinding.parseTrackName(path);this.node=PropertyBinding.findNode(rootNode,this.parsedPath.nodeName)||rootNode;this.rootNode=rootNode;this.getValue=this._getValue_unbound;this.setValue=this._setValue_unbound}static create(root,path,parsedPath){if(!(root&&root.isAnimationObjectGroup)){return new PropertyBinding(root,path,parsedPath)}else{return new PropertyBinding.Composite(root,path,parsedPath)}}static sanitizeNodeName(name){return name.replace(/\s/g,"_").replace(_reservedRe,"")}static parseTrackName(trackName){const matches=_trackRe.exec(trackName);if(matches===null){throw new Error("PropertyBinding: Cannot parse trackName: "+trackName)}const results={nodeName:matches[2],objectName:matches[3],objectIndex:matches[4],propertyName:matches[5],propertyIndex:matches[6]};const lastDot=results.nodeName&&results.nodeName.lastIndexOf(".");if(lastDot!==undefined&&lastDot!==-1){const objectName=results.nodeName.substring(lastDot+1);if(_supportedObjectNames.indexOf(objectName)!==-1){results.nodeName=results.nodeName.substring(0,lastDot);results.objectName=objectName}}if(results.propertyName===null||results.propertyName.length===0){throw new Error("PropertyBinding: can not parse propertyName from trackName: "+trackName)}return results}static findNode(root,nodeName){if(nodeName===undefined||nodeName===""||nodeName==="."||nodeName===-1||nodeName===root.name||nodeName===root.uuid){return root}if(root.skeleton){const bone=root.skeleton.getBoneByName(nodeName);if(bone!==undefined){return bone}}if(root.children){const searchNodeSubtree=function(children){for(let i=0;i0){const interpolants=this._interpolants;const propertyMixers=this._propertyBindings;switch(this.blendMode){case AdditiveAnimationBlendMode:for(let j=0,m=interpolants.length;j!==m;++j){interpolants[j].evaluate(clipTime);propertyMixers[j].accumulateAdditive(weight)}break;case NormalAnimationBlendMode:default:for(let j=0,m=interpolants.length;j!==m;++j){interpolants[j].evaluate(clipTime);propertyMixers[j].accumulate(accuIndex,weight)}}}}_updateWeight(time){let weight=0;if(this.enabled){weight=this.weight;const interpolant=this._weightInterpolant;if(interpolant!==null){const interpolantValue=interpolant.evaluate(time)[0];weight*=interpolantValue;if(time>interpolant.parameterPositions[1]){this.stopFading();if(interpolantValue===0){this.enabled=false}}}}this._effectiveWeight=weight;return weight}_updateTimeScale(time){let timeScale=0;if(!this.paused){timeScale=this.timeScale;const interpolant=this._timeScaleInterpolant;if(interpolant!==null){const interpolantValue=interpolant.evaluate(time)[0];timeScale*=interpolantValue;if(time>interpolant.parameterPositions[1]){this.stopWarping();if(timeScale===0){this.paused=true}else{this.timeScale=timeScale}}}}this._effectiveTimeScale=timeScale;return timeScale}_updateTime(deltaTime){const duration=this._clip.duration;const loop=this.loop;let time=this.time+deltaTime;let loopCount=this._loopCount;const pingPong=loop===LoopPingPong;if(deltaTime===0){if(loopCount===-1)return time;return pingPong&&(loopCount&1)===1?duration-time:time}if(loop===LoopOnce){if(loopCount===-1){this._loopCount=0;this._setEndings(true,true,false)}handle_stop:{if(time>=duration){time=duration}else if(time<0){time=0}else{this.time=time;break handle_stop}if(this.clampWhenFinished)this.paused=true;else this.enabled=false;this.time=time;this._mixer.dispatchEvent({type:"finished",action:this,direction:deltaTime<0?-1:1})}}else{if(loopCount===-1){if(deltaTime>=0){loopCount=0;this._setEndings(true,this.repetitions===0,pingPong)}else{this._setEndings(this.repetitions===0,true,pingPong)}}if(time>=duration||time<0){const loopDelta=Math.floor(time/duration);time-=duration*loopDelta;loopCount+=Math.abs(loopDelta);const pending=this.repetitions-loopCount;if(pending<=0){if(this.clampWhenFinished)this.paused=true;else this.enabled=false;time=deltaTime>0?duration:0;this.time=time;this._mixer.dispatchEvent({type:"finished",action:this,direction:deltaTime>0?1:-1})}else{if(pending===1){const atStart=deltaTime<0;this._setEndings(atStart,!atStart,pingPong)}else{this._setEndings(false,false,pingPong)}this._loopCount=loopCount;this.time=time;this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:loopDelta})}}else{this.time=time}if(pingPong&&(loopCount&1)===1){return duration-time}}return time}_setEndings(atStart,atEnd,pingPong){const settings=this._interpolantSettings;if(pingPong){settings.endingStart=ZeroSlopeEnding;settings.endingEnd=ZeroSlopeEnding}else{if(atStart){settings.endingStart=this.zeroSlopeAtStart?ZeroSlopeEnding:ZeroCurvatureEnding}else{settings.endingStart=WrapAroundEnding}if(atEnd){settings.endingEnd=this.zeroSlopeAtEnd?ZeroSlopeEnding:ZeroCurvatureEnding}else{settings.endingEnd=WrapAroundEnding}}}_scheduleFading(duration,weightNow,weightThen){const mixer=this._mixer,now=mixer.time;let interpolant=this._weightInterpolant;if(interpolant===null){interpolant=mixer._lendControlInterpolant();this._weightInterpolant=interpolant}const times=interpolant.parameterPositions,values=interpolant.sampleValues;times[0]=now;values[0]=weightNow;times[1]=now+duration;values[1]=weightThen;return this}}class AnimationMixer extends EventDispatcher$1{constructor(root){super();this._root=root;this._initMemoryManager();this._accuIndex=0;this.time=0;this.timeScale=1}_bindAction(action,prototypeAction){const root=action._localRoot||this._root,tracks=action._clip.tracks,nTracks=tracks.length,bindings=action._propertyBindings,interpolants=action._interpolants,rootUuid=root.uuid,bindingsByRoot=this._bindingsByRootAndName;let bindingsByName=bindingsByRoot[rootUuid];if(bindingsByName===undefined){bindingsByName={};bindingsByRoot[rootUuid]=bindingsByName}for(let i=0;i!==nTracks;++i){const track=tracks[i],trackName=track.name;let binding=bindingsByName[trackName];if(binding!==undefined){++binding.referenceCount;bindings[i]=binding}else{binding=bindings[i];if(binding!==undefined){if(binding._cacheIndex===null){++binding.referenceCount;this._addInactiveBinding(binding,rootUuid,trackName)}continue}const path=prototypeAction&&prototypeAction._propertyBindings[i].binding.parsedPath;binding=new PropertyMixer(PropertyBinding.create(root,trackName,path),track.ValueTypeName,track.getValueSize());++binding.referenceCount;this._addInactiveBinding(binding,rootUuid,trackName);bindings[i]=binding}interpolants[i].resultBuffer=binding.buffer}}_activateAction(action){if(!this._isActiveAction(action)){if(action._cacheIndex===null){const rootUuid=(action._localRoot||this._root).uuid,clipUuid=action._clip.uuid,actionsForClip=this._actionsByClip[clipUuid];this._bindAction(action,actionsForClip&&actionsForClip.knownActions[0]);this._addInactiveAction(action,clipUuid,rootUuid)}const bindings=action._propertyBindings;for(let i=0,n=bindings.length;i!==n;++i){const binding=bindings[i];if(binding.useCount++===0){this._lendBinding(binding);binding.saveOriginalState()}}this._lendAction(action)}}_deactivateAction(action){if(this._isActiveAction(action)){const bindings=action._propertyBindings;for(let i=0,n=bindings.length;i!==n;++i){const binding=bindings[i];if(--binding.useCount===0){binding.restoreOriginalState();this._takeBackBinding(binding)}}this._takeBackAction(action)}}_initMemoryManager(){this._actions=[];this._nActiveActions=0;this._actionsByClip={};this._bindings=[];this._nActiveBindings=0;this._bindingsByRootAndName={};this._controlInterpolants=[];this._nActiveControlInterpolants=0;const scope=this;this.stats={actions:{get total(){return scope._actions.length},get inUse(){return scope._nActiveActions}},bindings:{get total(){return scope._bindings.length},get inUse(){return scope._nActiveBindings}},controlInterpolants:{get total(){return scope._controlInterpolants.length},get inUse(){return scope._nActiveControlInterpolants}}}}_isActiveAction(action){const index=action._cacheIndex;return index!==null&&index=0;--i){actions[i].stop()}return this}update(deltaTime){deltaTime*=this.timeScale;const actions=this._actions,nActions=this._nActiveActions,time=this.time+=deltaTime,timeDirection=Math.sign(deltaTime),accuIndex=this._accuIndex^=1;for(let i=0;i!==nActions;++i){const action=actions[i];action._update(time,deltaTime,timeDirection,accuIndex)}const bindings=this._bindings,nBindings=this._nActiveBindings;for(let i=0;i!==nBindings;++i){bindings[i].apply(accuIndex)}return this}setTime(timeInSeconds){this.time=0;for(let i=0;i>-e-14;_baseTable[i|256]=1024>>-e-14|32768;_shiftTable[i]=-e-1;_shiftTable[i|256]=-e-1}else if(e<=15){_baseTable[i]=e+15<<10;_baseTable[i|256]=e+15<<10|32768;_shiftTable[i]=13;_shiftTable[i|256]=13}else if(e<128){_baseTable[i]=31744;_baseTable[i|256]=64512;_shiftTable[i]=24;_shiftTable[i|256]=24}else{_baseTable[i]=31744;_baseTable[i|256]=64512;_shiftTable[i]=13;_shiftTable[i|256]=13}}const _mantissaTable=new Uint32Array(2048);const _exponentTable=new Uint32Array(64);const _offsetTable=new Uint32Array(64);for(let i=1;i<1024;++i){let m=i<<13;let e=0;while((m&8388608)===0){m<<=1;e-=8388608}m&=~8388608;e+=947912704;_mantissaTable[i]=m|e}for(let i=1024;i<2048;++i){_mantissaTable[i]=939524096+(i-1024<<13)}for(let i=1;i<31;++i){_exponentTable[i]=i<<23}_exponentTable[31]=1199570944;_exponentTable[32]=2147483648;for(let i=33;i<63;++i){_exponentTable[i]=2147483648+(i-32<<23)}_exponentTable[63]=3347054592;for(let i=1;i<64;++i){if(i!==32){_offsetTable[i]=1024}}Curve.create=function(construct,getPoint){console.log("THREE.Curve.create() has been deprecated");construct.prototype=Object.create(Curve.prototype);construct.prototype.constructor=construct;construct.prototype.getPoint=getPoint;return construct};Path.prototype.fromPoints=function(points){console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints().");return this.setFromPoints(points)};GridHelper.prototype.setColors=function(){console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.")};SkeletonHelper.prototype.update=function(){console.error("THREE.SkeletonHelper: update() no longer needs to be called.")};Loader.prototype.extractUrlBase=function(url){console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.");return LoaderUtils.extractUrlBase(url)};Loader.Handlers={add:function(){console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.")},get:function(){console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.")}};Box3.prototype.center=function(optionalTarget){console.warn("THREE.Box3: .center() has been renamed to .getCenter().");return this.getCenter(optionalTarget)};Box3.prototype.empty=function(){console.warn("THREE.Box3: .empty() has been renamed to .isEmpty().");return this.isEmpty()};Box3.prototype.isIntersectionBox=function(box){console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().");return this.intersectsBox(box)};Box3.prototype.isIntersectionSphere=function(sphere){console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().");return this.intersectsSphere(sphere)};Box3.prototype.size=function(optionalTarget){console.warn("THREE.Box3: .size() has been renamed to .getSize().");return this.getSize(optionalTarget)};Euler.prototype.toVector3=function(){console.error("THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead")};Sphere.prototype.empty=function(){console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty().");return this.isEmpty()};Frustum.prototype.setFromMatrix=function(m){console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().");return this.setFromProjectionMatrix(m)};Matrix3.prototype.flattenToArrayOffset=function(array,offset){console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.");return this.toArray(array,offset)};Matrix3.prototype.multiplyVector3=function(vector){console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return vector.applyMatrix3(this)};Matrix3.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix3: .multiplyVector3Array() has been removed.")};Matrix3.prototype.applyToBufferAttribute=function(attribute){console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.");return attribute.applyMatrix3(this)};Matrix3.prototype.applyToVector3Array=function(){console.error("THREE.Matrix3: .applyToVector3Array() has been removed.")};Matrix3.prototype.getInverse=function(matrix){console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.");return this.copy(matrix).invert()};Matrix4.prototype.extractPosition=function(m){console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().");return this.copyPosition(m)};Matrix4.prototype.flattenToArrayOffset=function(array,offset){console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.");return this.toArray(array,offset)};Matrix4.prototype.getPosition=function(){console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.");return(new Vector3).setFromMatrixColumn(this,3)};Matrix4.prototype.setRotationFromQuaternion=function(q){console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().");return this.makeRotationFromQuaternion(q)};Matrix4.prototype.multiplyToArray=function(){console.warn("THREE.Matrix4: .multiplyToArray() has been removed.")};Matrix4.prototype.multiplyVector3=function(vector){console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.");return vector.applyMatrix4(this)};Matrix4.prototype.multiplyVector4=function(vector){console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return vector.applyMatrix4(this)};Matrix4.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix4: .multiplyVector3Array() has been removed.")};Matrix4.prototype.rotateAxis=function(v){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");v.transformDirection(this)};Matrix4.prototype.crossVector=function(vector){console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");return vector.applyMatrix4(this)};Matrix4.prototype.translate=function(){console.error("THREE.Matrix4: .translate() has been removed.")};Matrix4.prototype.rotateX=function(){console.error("THREE.Matrix4: .rotateX() has been removed.")};Matrix4.prototype.rotateY=function(){console.error("THREE.Matrix4: .rotateY() has been removed.")};Matrix4.prototype.rotateZ=function(){console.error("THREE.Matrix4: .rotateZ() has been removed.")};Matrix4.prototype.rotateByAxis=function(){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")};Matrix4.prototype.applyToBufferAttribute=function(attribute){console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.");return attribute.applyMatrix4(this)};Matrix4.prototype.applyToVector3Array=function(){console.error("THREE.Matrix4: .applyToVector3Array() has been removed.")};Matrix4.prototype.makeFrustum=function(left,right,bottom,top,near,far){console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.");return this.makePerspective(left,right,top,bottom,near,far)};Matrix4.prototype.getInverse=function(matrix){console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.");return this.copy(matrix).invert()};Plane.prototype.isIntersectionLine=function(line){console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().");return this.intersectsLine(line)};Quaternion.prototype.multiplyVector3=function(vector){console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");return vector.applyQuaternion(this)};Quaternion.prototype.inverse=function(){console.warn("THREE.Quaternion: .inverse() has been renamed to invert().");return this.invert()};Ray.prototype.isIntersectionBox=function(box){console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().");return this.intersectsBox(box)};Ray.prototype.isIntersectionPlane=function(plane){console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().");return this.intersectsPlane(plane)};Ray.prototype.isIntersectionSphere=function(sphere){console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().");return this.intersectsSphere(sphere)};Triangle.prototype.area=function(){console.warn("THREE.Triangle: .area() has been renamed to .getArea().");return this.getArea()};Triangle.prototype.barycoordFromPoint=function(point,target){console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().");return this.getBarycoord(point,target)};Triangle.prototype.midpoint=function(target){console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint().");return this.getMidpoint(target)};Triangle.prototypenormal=function(target){console.warn("THREE.Triangle: .normal() has been renamed to .getNormal().");return this.getNormal(target)};Triangle.prototype.plane=function(target){console.warn("THREE.Triangle: .plane() has been renamed to .getPlane().");return this.getPlane(target)};Triangle.barycoordFromPoint=function(point,a,b,c,target){console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().");return Triangle.getBarycoord(point,a,b,c,target)};Triangle.normal=function(a,b,c,target){console.warn("THREE.Triangle: .normal() has been renamed to .getNormal().");return Triangle.getNormal(a,b,c,target)};Shape.prototype.extractAllPoints=function(divisions){console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.");return this.extractPoints(divisions)};Shape.prototype.extrude=function(options){console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.");return new ExtrudeGeometry(this,options)};Shape.prototype.makeGeometry=function(options){console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.");return new ShapeGeometry(this,options)};Vector2.prototype.fromAttribute=function(attribute,index,offset){console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().");return this.fromBufferAttribute(attribute,index,offset)};Vector2.prototype.distanceToManhattan=function(v){console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().");return this.manhattanDistanceTo(v)};Vector2.prototype.lengthManhattan=function(){console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().");return this.manhattanLength()};Vector3.prototype.setEulerFromRotationMatrix=function(){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")};Vector3.prototype.setEulerFromQuaternion=function(){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")};Vector3.prototype.getPositionFromMatrix=function(m){console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().");return this.setFromMatrixPosition(m)};Vector3.prototype.getScaleFromMatrix=function(m){console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().");return this.setFromMatrixScale(m)};Vector3.prototype.getColumnFromMatrix=function(index,matrix){console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().");return this.setFromMatrixColumn(matrix,index)};Vector3.prototype.applyProjection=function(m){console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.");return this.applyMatrix4(m)};Vector3.prototype.fromAttribute=function(attribute,index,offset){console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().");return this.fromBufferAttribute(attribute,index,offset)};Vector3.prototype.distanceToManhattan=function(v){console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().");return this.manhattanDistanceTo(v)};Vector3.prototype.lengthManhattan=function(){console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().");return this.manhattanLength()};Vector4.prototype.fromAttribute=function(attribute,index,offset){console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().");return this.fromBufferAttribute(attribute,index,offset)};Vector4.prototype.lengthManhattan=function(){console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().");return this.manhattanLength()};Object3D.prototype.getChildByName=function(name){console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().");return this.getObjectByName(name)};Object3D.prototype.renderDepth=function(){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")};Object3D.prototype.translate=function(distance,axis){console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.");return this.translateOnAxis(axis,distance)};Object3D.prototype.getWorldRotation=function(){console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.")};Object3D.prototype.applyMatrix=function(matrix){console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().");return this.applyMatrix4(matrix)};Object.defineProperties(Object3D.prototype,{eulerOrder:{get:function(){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order.");return this.rotation.order},set:function(value){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order.");this.rotation.order=value}},useQuaternion:{get:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")}}});Mesh.prototype.setDrawMode=function(){console.error("THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")};Object.defineProperties(Mesh.prototype,{drawMode:{get:function(){console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.");return TrianglesDrawMode},set:function(){console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")}}});SkinnedMesh.prototype.initBones=function(){console.error("THREE.SkinnedMesh: initBones() has been removed.")};PerspectiveCamera.prototype.setLens=function(focalLength,filmGauge){console.warn("THREE.PerspectiveCamera.setLens is deprecated. "+"Use .setFocalLength and .filmGauge for a photographic setup.");if(filmGauge!==undefined)this.filmGauge=filmGauge;this.setFocalLength(focalLength)};Object.defineProperties(Light.prototype,{onlyShadow:{set:function(){console.warn("THREE.Light: .onlyShadow has been removed.")}},shadowCameraFov:{set:function(value){console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov.");this.shadow.camera.fov=value}},shadowCameraLeft:{set:function(value){console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left.");this.shadow.camera.left=value}},shadowCameraRight:{set:function(value){console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right.");this.shadow.camera.right=value}},shadowCameraTop:{set:function(value){console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top.");this.shadow.camera.top=value}},shadowCameraBottom:{set:function(value){console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.");this.shadow.camera.bottom=value}},shadowCameraNear:{set:function(value){console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near.");this.shadow.camera.near=value}},shadowCameraFar:{set:function(value){console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far.");this.shadow.camera.far=value}},shadowCameraVisible:{set:function(){console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.")}},shadowBias:{set:function(value){console.warn("THREE.Light: .shadowBias is now .shadow.bias.");this.shadow.bias=value}},shadowDarkness:{set:function(){console.warn("THREE.Light: .shadowDarkness has been removed.")}},shadowMapWidth:{set:function(value){console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.");this.shadow.mapSize.width=value}},shadowMapHeight:{set:function(value){console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.");this.shadow.mapSize.height=value}}});Object.defineProperties(BufferAttribute.prototype,{length:{get:function(){console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead.");return this.array.length}},dynamic:{get:function(){console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.");return this.usage===DynamicDrawUsage},set:function(){console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.");this.setUsage(DynamicDrawUsage)}}});BufferAttribute.prototype.setDynamic=function(value){console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.");this.setUsage(value===true?DynamicDrawUsage:StaticDrawUsage);return this};BufferAttribute.prototype.copyIndicesArray=function(){console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed.")},BufferAttribute.prototype.setArray=function(){console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")};BufferGeometry.prototype.addIndex=function(index){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().");this.setIndex(index)};BufferGeometry.prototype.addAttribute=function(name,attribute){console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().");if(!(attribute&&attribute.isBufferAttribute)&&!(attribute&&attribute.isInterleavedBufferAttribute)){console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).");return this.setAttribute(name,new BufferAttribute(arguments[1],arguments[2]))}if(name==="index"){console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.");this.setIndex(attribute);return this}return this.setAttribute(name,attribute)};BufferGeometry.prototype.addDrawCall=function(start,count,indexOffset){if(indexOffset!==undefined){console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.")}console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup().");this.addGroup(start,count)};BufferGeometry.prototype.clearDrawCalls=function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().");this.clearGroups()};BufferGeometry.prototype.computeOffsets=function(){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")};BufferGeometry.prototype.removeAttribute=function(name){console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().");return this.deleteAttribute(name)};BufferGeometry.prototype.applyMatrix=function(matrix){console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().");return this.applyMatrix4(matrix)};Object.defineProperties(BufferGeometry.prototype,{drawcalls:{get:function(){console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups.");return this.groups}},offsets:{get:function(){console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups.");return this.groups}}});InterleavedBuffer.prototype.setDynamic=function(value){console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.");this.setUsage(value===true?DynamicDrawUsage:StaticDrawUsage);return this};InterleavedBuffer.prototype.setArray=function(){console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")};ExtrudeGeometry.prototype.getArrays=function(){console.error("THREE.ExtrudeGeometry: .getArrays() has been removed.")};ExtrudeGeometry.prototype.addShapeList=function(){console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed.")};ExtrudeGeometry.prototype.addShape=function(){console.error("THREE.ExtrudeGeometry: .addShape() has been removed.")};Scene.prototype.dispose=function(){console.error("THREE.Scene: .dispose() has been removed.")};Object.defineProperties(Material.prototype,{wrapAround:{get:function(){console.warn("THREE.Material: .wrapAround has been removed.")},set:function(){console.warn("THREE.Material: .wrapAround has been removed.")}},overdraw:{get:function(){console.warn("THREE.Material: .overdraw has been removed.")},set:function(){console.warn("THREE.Material: .overdraw has been removed.")}},wrapRGB:{get:function(){console.warn("THREE.Material: .wrapRGB has been removed.");return new Color}},shading:{get:function(){console.error("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.")},set:function(value){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.");this.flatShading=value===FlatShading}},stencilMask:{get:function(){console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead.");return this.stencilFuncMask},set:function(value){console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead.");this.stencilFuncMask=value}},vertexTangents:{get:function(){console.warn("THREE."+this.type+": .vertexTangents has been removed.")},set:function(){console.warn("THREE."+this.type+": .vertexTangents has been removed.")}}});Object.defineProperties(ShaderMaterial.prototype,{derivatives:{get:function(){console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.");return this.extensions.derivatives},set:function(value){console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.");this.extensions.derivatives=value}}});WebGLRenderer.prototype.clearTarget=function(renderTarget,color,depth,stencil){console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.");this.setRenderTarget(renderTarget);this.clear(color,depth,stencil)};WebGLRenderer.prototype.animate=function(callback){console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop().");this.setAnimationLoop(callback)};WebGLRenderer.prototype.getCurrentRenderTarget=function(){console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().");return this.getRenderTarget()};WebGLRenderer.prototype.getMaxAnisotropy=function(){console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().");return this.capabilities.getMaxAnisotropy()};WebGLRenderer.prototype.getPrecision=function(){console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.");return this.capabilities.precision};WebGLRenderer.prototype.resetGLState=function(){console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset().");return this.state.reset()};WebGLRenderer.prototype.supportsFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' ).");return this.extensions.get("OES_texture_float")};WebGLRenderer.prototype.supportsHalfFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' ).");return this.extensions.get("OES_texture_half_float")};WebGLRenderer.prototype.supportsStandardDerivatives=function(){console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' ).");return this.extensions.get("OES_standard_derivatives")};WebGLRenderer.prototype.supportsCompressedTextureS3TC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' ).");return this.extensions.get("WEBGL_compressed_texture_s3tc")};WebGLRenderer.prototype.supportsCompressedTexturePVRTC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' ).");return this.extensions.get("WEBGL_compressed_texture_pvrtc")};WebGLRenderer.prototype.supportsBlendMinMax=function(){console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' ).");return this.extensions.get("EXT_blend_minmax")};WebGLRenderer.prototype.supportsVertexTextures=function(){console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.");return this.capabilities.vertexTextures};WebGLRenderer.prototype.supportsInstancedArrays=function(){console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' ).");return this.extensions.get("ANGLE_instanced_arrays")};WebGLRenderer.prototype.enableScissorTest=function(boolean){console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().");this.setScissorTest(boolean)};WebGLRenderer.prototype.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};WebGLRenderer.prototype.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};WebGLRenderer.prototype.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};WebGLRenderer.prototype.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")};WebGLRenderer.prototype.setFaceCulling=function(){console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed.")};WebGLRenderer.prototype.allocTextureUnit=function(){console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed.")};WebGLRenderer.prototype.setTexture=function(){console.warn("THREE.WebGLRenderer: .setTexture() has been removed.")};WebGLRenderer.prototype.setTexture2D=function(){console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed.")};WebGLRenderer.prototype.setTextureCube=function(){console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed.")};WebGLRenderer.prototype.getActiveMipMapLevel=function(){console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().");return this.getActiveMipmapLevel()};Object.defineProperties(WebGLRenderer.prototype,{shadowMapEnabled:{get:function(){return this.shadowMap.enabled},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.");this.shadowMap.enabled=value}},shadowMapType:{get:function(){return this.shadowMap.type},set:function(value){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.");this.shadowMap.type=value}},shadowMapCullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.");return undefined},set:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.")}},context:{get:function(){console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.");return this.getContext()}},vr:{get:function(){console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr");return this.xr}},gammaInput:{get:function(){console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.");return false},set:function(){console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.")}},gammaOutput:{get:function(){console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.");return false},set:function(value){console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.");this.outputEncoding=value===true?sRGBEncoding:LinearEncoding}},toneMappingWhitePoint:{get:function(){console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.");return 1},set:function(){console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.")}},gammaFactor:{get:function(){console.warn("THREE.WebGLRenderer: .gammaFactor has been removed.");return 2},set:function(){console.warn("THREE.WebGLRenderer: .gammaFactor has been removed.")}}});Object.defineProperties(WebGLShadowMap.prototype,{cullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.");return undefined},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.")}},renderReverseSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.");return undefined},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.")}},renderSingleSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.");return undefined},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.")}}});Object.defineProperties(WebGLRenderTarget.prototype,{wrapS:{get:function(){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.");return this.texture.wrapS},set:function(value){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.");this.texture.wrapS=value}},wrapT:{get:function(){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.");return this.texture.wrapT},set:function(value){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.");this.texture.wrapT=value}},magFilter:{get:function(){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.");return this.texture.magFilter},set:function(value){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.");this.texture.magFilter=value}},minFilter:{get:function(){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.");return this.texture.minFilter},set:function(value){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.");this.texture.minFilter=value}},anisotropy:{get:function(){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.");return this.texture.anisotropy},set:function(value){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.");this.texture.anisotropy=value}},offset:{get:function(){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset.");return this.texture.offset},set:function(value){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset.");this.texture.offset=value}},repeat:{get:function(){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat.");return this.texture.repeat},set:function(value){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat.");this.texture.repeat=value}},format:{get:function(){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format.");return this.texture.format},set:function(value){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format.");this.texture.format=value}},type:{get:function(){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type.");return this.texture.type},set:function(value){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type.");this.texture.type=value}},generateMipmaps:{get:function(){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.");return this.texture.generateMipmaps},set:function(value){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.");this.texture.generateMipmaps=value}}});Audio.prototype.load=function(file){console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.");const scope=this;const audioLoader=new AudioLoader;audioLoader.load(file,(function(buffer){scope.setBuffer(buffer)}));return this};CubeCamera.prototype.updateCubeMap=function(renderer,scene){console.warn("THREE.CubeCamera: .updateCubeMap() is now .update().");return this.update(renderer,scene)};CubeCamera.prototype.clear=function(renderer,color,depth,stencil){console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear().");return this.renderTarget.clear(renderer,color,depth,stencil)};ImageUtils.crossOrigin=undefined;ImageUtils.loadTexture=function(url,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.");const loader=new TextureLoader;loader.setCrossOrigin(this.crossOrigin);const texture=loader.load(url,onLoad,undefined,onError);if(mapping)texture.mapping=mapping;return texture};ImageUtils.loadTextureCube=function(urls,mapping,onLoad,onError){console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.");const loader=new CubeTextureLoader;loader.setCrossOrigin(this.crossOrigin);const texture=loader.load(urls,onLoad,undefined,onError);if(mapping)texture.mapping=mapping;return texture};ImageUtils.loadCompressedTexture=function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")};ImageUtils.loadCompressedTextureCube=function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")};if(typeof __THREE_DEVTOOLS__!=="undefined"){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:REVISION}}))}if(typeof window!=="undefined"){if(window.__THREE__){console.warn("WARNING: Multiple instances of Three.js being imported.")}else{window.__THREE__=REVISION}}const _taskCache=new WeakMap;class DRACOLoader extends Loader{constructor(manager){super(manager);this.decoderPath="";this.decoderConfig={};this.decoderBinary=null;this.decoderPending=null;this.workerLimit=4;this.workerPool=[];this.workerNextTaskID=1;this.workerSourceURL="";this.defaultAttributeIDs={position:"POSITION",normal:"NORMAL",color:"COLOR",uv:"TEX_COORD"};this.defaultAttributeTypes={position:"Float32Array",normal:"Float32Array",color:"Float32Array",uv:"Float32Array"}}setDecoderPath(path){this.decoderPath=path;return this}setDecoderConfig(config){this.decoderConfig=config;return this}setWorkerLimit(workerLimit){this.workerLimit=workerLimit;return this}load(url,onLoad,onProgress,onError){const loader=new FileLoader(this.manager);loader.setPath(this.path);loader.setResponseType("arraybuffer");loader.setRequestHeader(this.requestHeader);loader.setWithCredentials(this.withCredentials);loader.load(url,(buffer=>{const taskConfig={attributeIDs:this.defaultAttributeIDs,attributeTypes:this.defaultAttributeTypes,useUniqueIDs:false};this.decodeGeometry(buffer,taskConfig).then(onLoad).catch(onError)}),onProgress,onError)}decodeDracoFile(buffer,callback,attributeIDs,attributeTypes){const taskConfig={attributeIDs:attributeIDs||this.defaultAttributeIDs,attributeTypes:attributeTypes||this.defaultAttributeTypes,useUniqueIDs:!!attributeIDs};this.decodeGeometry(buffer,taskConfig).then(callback)}decodeGeometry(buffer,taskConfig){for(const attribute in taskConfig.attributeTypes){const type=taskConfig.attributeTypes[attribute];if(type.BYTES_PER_ELEMENT!==undefined){taskConfig.attributeTypes[attribute]=type.name}}const taskKey=JSON.stringify(taskConfig);if(_taskCache.has(buffer)){const cachedTask=_taskCache.get(buffer);if(cachedTask.key===taskKey){return cachedTask.promise}else if(buffer.byteLength===0){throw new Error("THREE.DRACOLoader: Unable to re-decode a buffer with different "+"settings. Buffer has already been transferred.")}}let worker;const taskID=this.workerNextTaskID++;const taskCost=buffer.byteLength;const geometryPending=this._getWorker(taskID,taskCost).then((_worker=>{worker=_worker;return new Promise(((resolve,reject)=>{worker._callbacks[taskID]={resolve:resolve,reject:reject};worker.postMessage({type:"decode",id:taskID,taskConfig:taskConfig,buffer:buffer},[buffer])}))})).then((message=>this._createGeometry(message.geometry)));geometryPending.catch((()=>true)).then((()=>{if(worker&&taskID){this._releaseTask(worker,taskID)}}));_taskCache.set(buffer,{key:taskKey,promise:geometryPending});return geometryPending}_createGeometry(geometryData){const geometry=new BufferGeometry;if(geometryData.index){geometry.setIndex(new BufferAttribute(geometryData.index.array,1))}for(let i=0;i{loader.load(url,resolve,undefined,reject)}))}preload(){this._initDecoder();return this}_initDecoder(){if(this.decoderPending)return this.decoderPending;const useJS=typeof WebAssembly!=="object"||this.decoderConfig.type==="js";const librariesPending=[];if(useJS){librariesPending.push(this._loadLibrary("draco_decoder.js","text"))}else{librariesPending.push(this._loadLibrary("draco_wasm_wrapper.js","text"));librariesPending.push(this._loadLibrary("draco_decoder.wasm","arraybuffer"))}this.decoderPending=Promise.all(librariesPending).then((libraries=>{const jsContent=libraries[0];if(!useJS){this.decoderConfig.wasmBinary=libraries[1]}const fn=DRACOWorker.toString();const body=["/* draco decoder */",jsContent,"","/* worker */",fn.substring(fn.indexOf("{")+1,fn.lastIndexOf("}"))].join("\n");this.workerSourceURL=URL.createObjectURL(new Blob([body]))}));return this.decoderPending}_getWorker(taskID,taskCost){return this._initDecoder().then((()=>{if(this.workerPool.lengthb._taskLoad?-1:1}))}const worker=this.workerPool[this.workerPool.length-1];worker._taskCosts[taskID]=taskCost;worker._taskLoad+=taskCost;return worker}))}_releaseTask(worker,taskID){worker._taskLoad-=worker._taskCosts[taskID];delete worker._callbacks[taskID];delete worker._taskCosts[taskID]}debug(){console.log("Task load: ",this.workerPool.map((worker=>worker._taskLoad)))}dispose(){for(let i=0;i{const draco=module.draco;const decoder=new draco.Decoder;const decoderBuffer=new draco.DecoderBuffer;decoderBuffer.Init(new Int8Array(buffer),buffer.byteLength);try{const geometry=decodeGeometry(draco,decoder,decoderBuffer,taskConfig);const buffers=geometry.attributes.map((attr=>attr.array.buffer));if(geometry.index)buffers.push(geometry.index.array.buffer);self.postMessage({type:"decode",id:message.id,geometry:geometry},buffers)}catch(error){console.error(error);self.postMessage({type:"error",id:message.id,error:error.message})}finally{draco.destroy(decoderBuffer);draco.destroy(decoder)}}));break}};function decodeGeometry(draco,decoder,decoderBuffer,taskConfig){const attributeIDs=taskConfig.attributeIDs;const attributeTypes=taskConfig.attributeTypes;let dracoGeometry;let decodingStatus;const geometryType=decoder.GetEncodedGeometryType(decoderBuffer);if(geometryType===draco.TRIANGULAR_MESH){dracoGeometry=new draco.Mesh;decodingStatus=decoder.DecodeBufferToMesh(decoderBuffer,dracoGeometry)}else if(geometryType===draco.POINT_CLOUD){dracoGeometry=new draco.PointCloud;decodingStatus=decoder.DecodeBufferToPointCloud(decoderBuffer,dracoGeometry)}else{throw new Error("THREE.DRACOLoader: Unexpected geometry type.")}if(!decodingStatus.ok()||dracoGeometry.ptr===0){throw new Error("THREE.DRACOLoader: Decoding failed: "+decodingStatus.error_msg())}const geometry={index:null,attributes:[]};for(const attributeName in attributeIDs){const attributeType=self[attributeTypes[attributeName]];let attribute;let attributeID;if(taskConfig.useUniqueIDs){attributeID=attributeIDs[attributeName];attribute=decoder.GetAttributeByUniqueId(dracoGeometry,attributeID)}else{attributeID=decoder.GetAttributeId(dracoGeometry,draco[attributeIDs[attributeName]]);if(attributeID===-1)continue;attribute=decoder.GetAttribute(dracoGeometry,attributeID)}geometry.attributes.push(decodeAttribute(draco,decoder,dracoGeometry,attributeName,attributeType,attribute))}if(geometryType===draco.TRIANGULAR_MESH){geometry.index=decodeIndex(draco,decoder,dracoGeometry)}draco.destroy(dracoGeometry);return geometry}function decodeIndex(draco,decoder,dracoGeometry){const numFaces=dracoGeometry.num_faces();const numIndices=numFaces*3;const byteLength=numIndices*4;const ptr=draco._malloc(byteLength);decoder.GetTrianglesUInt32Array(dracoGeometry,byteLength,ptr);const index=new Uint32Array(draco.HEAPF32.buffer,ptr,numIndices).slice();draco._free(ptr);return{array:index,itemSize:1}}function decodeAttribute(draco,decoder,dracoGeometry,attributeName,attributeType,attribute){const numComponents=attribute.num_components();const numPoints=dracoGeometry.num_points();const numValues=numPoints*numComponents;const byteLength=numValues*attributeType.BYTES_PER_ELEMENT;const dataType=getDracoDataType(draco,attributeType);const ptr=draco._malloc(byteLength);decoder.GetAttributeDataArrayForAllPoints(dracoGeometry,attribute,dataType,byteLength,ptr);const array=new attributeType(draco.HEAPF32.buffer,ptr,numValues).slice();draco._free(ptr);return{name:attributeName,array:array,itemSize:numComponents}}function getDracoDataType(draco,attributeType){switch(attributeType){case Float32Array:return draco.DT_FLOAT32;case Int8Array:return draco.DT_INT8;case Int16Array:return draco.DT_INT16;case Int32Array:return draco.DT_INT32;case Uint8Array:return draco.DT_UINT8;case Uint16Array:return draco.DT_UINT16;case Uint32Array:return draco.DT_UINT32}}}class GLTFLoader extends Loader{constructor(manager){super(manager);this.dracoLoader=null;this.ktx2Loader=null;this.meshoptDecoder=null;this.pluginCallbacks=[];this.register((function(parser){return new GLTFMaterialsClearcoatExtension(parser)}));this.register((function(parser){return new GLTFTextureBasisUExtension(parser)}));this.register((function(parser){return new GLTFTextureWebPExtension(parser)}));this.register((function(parser){return new GLTFMaterialsSheenExtension(parser)}));this.register((function(parser){return new GLTFMaterialsTransmissionExtension(parser)}));this.register((function(parser){return new GLTFMaterialsVolumeExtension(parser)}));this.register((function(parser){return new GLTFMaterialsIorExtension(parser)}));this.register((function(parser){return new GLTFMaterialsSpecularExtension(parser)}));this.register((function(parser){return new GLTFLightsExtension(parser)}));this.register((function(parser){return new GLTFMeshoptCompression(parser)}))}load(url,onLoad,onProgress,onError){const scope=this;let resourcePath;if(this.resourcePath!==""){resourcePath=this.resourcePath}else if(this.path!==""){resourcePath=this.path}else{resourcePath=LoaderUtils.extractUrlBase(url)}this.manager.itemStart(url);const _onError=function(e){if(onError){onError(e)}else{console.error(e)}scope.manager.itemError(url);scope.manager.itemEnd(url)};const loader=new FileLoader(this.manager);loader.setPath(this.path);loader.setResponseType("arraybuffer");loader.setRequestHeader(this.requestHeader);loader.setWithCredentials(this.withCredentials);loader.load(url,(function(data){try{scope.parse(data,resourcePath,(function(gltf){onLoad(gltf);scope.manager.itemEnd(url)}),_onError)}catch(e){_onError(e)}}),onProgress,_onError)}setDRACOLoader(dracoLoader){this.dracoLoader=dracoLoader;return this}setDDSLoader(){throw new Error('THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".')}setKTX2Loader(ktx2Loader){this.ktx2Loader=ktx2Loader;return this}setMeshoptDecoder(meshoptDecoder){this.meshoptDecoder=meshoptDecoder;return this}register(callback){if(this.pluginCallbacks.indexOf(callback)===-1){this.pluginCallbacks.push(callback)}return this}unregister(callback){if(this.pluginCallbacks.indexOf(callback)!==-1){this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback),1)}return this}parse(data,path,onLoad,onError){let content;const extensions={};const plugins={};if(typeof data==="string"){content=data}else{const magic=LoaderUtils.decodeText(new Uint8Array(data,0,4));if(magic===BINARY_EXTENSION_HEADER_MAGIC){try{extensions[EXTENSIONS$1.KHR_BINARY_GLTF]=new GLTFBinaryExtension$1(data)}catch(error){if(onError)onError(error);return}content=extensions[EXTENSIONS$1.KHR_BINARY_GLTF].content}else{content=LoaderUtils.decodeText(new Uint8Array(data))}}const json=JSON.parse(content);if(json.asset===undefined||json.asset.version[0]<2){if(onError)onError(new Error("THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported."));return}const parser=new GLTFParser$1(json,{path:path||this.resourcePath||"",crossOrigin:this.crossOrigin,requestHeader:this.requestHeader,manager:this.manager,ktx2Loader:this.ktx2Loader,meshoptDecoder:this.meshoptDecoder});parser.fileLoader.setRequestHeader(this.requestHeader);for(let i=0;i=0&&plugins[extensionName]===undefined){console.warn('THREE.GLTFLoader: Unknown extension "'+extensionName+'".')}}}}parser.setExtensions(extensions);parser.setPlugins(plugins);parser.parse(onLoad,onError)}parseAsync(data,path){const scope=this;return new Promise((function(resolve,reject){scope.parse(data,path,resolve,reject)}))}}function GLTFRegistry$1(){let objects={};return{get:function(key){return objects[key]},add:function(key,object){objects[key]=object},remove:function(key){delete objects[key]},removeAll:function(){objects={}}}}const EXTENSIONS$1={KHR_BINARY_GLTF:"KHR_binary_glTF",KHR_DRACO_MESH_COMPRESSION:"KHR_draco_mesh_compression",KHR_LIGHTS_PUNCTUAL:"KHR_lights_punctual",KHR_MATERIALS_CLEARCOAT:"KHR_materials_clearcoat",KHR_MATERIALS_IOR:"KHR_materials_ior",KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:"KHR_materials_pbrSpecularGlossiness",KHR_MATERIALS_SHEEN:"KHR_materials_sheen",KHR_MATERIALS_SPECULAR:"KHR_materials_specular",KHR_MATERIALS_TRANSMISSION:"KHR_materials_transmission",KHR_MATERIALS_UNLIT:"KHR_materials_unlit",KHR_MATERIALS_VOLUME:"KHR_materials_volume",KHR_TEXTURE_BASISU:"KHR_texture_basisu",KHR_TEXTURE_TRANSFORM:"KHR_texture_transform",KHR_MESH_QUANTIZATION:"KHR_mesh_quantization",EXT_TEXTURE_WEBP:"EXT_texture_webp",EXT_MESHOPT_COMPRESSION:"EXT_meshopt_compression"};class GLTFLightsExtension{constructor(parser){this.parser=parser;this.name=EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL;this.cache={refs:{},uses:{}}}_markDefs(){const parser=this.parser;const nodeDefs=this.parser.json.nodes||[];for(let nodeIndex=0,nodeLength=nodeDefs.length;nodeIndex=0){throw new Error("THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures")}else{return null}}return parser.loadTextureImage(textureIndex,extension.source,loader)}}class GLTFTextureWebPExtension{constructor(parser){this.parser=parser;this.name=EXTENSIONS$1.EXT_TEXTURE_WEBP;this.isSupported=null}loadTexture(textureIndex){const name=this.name;const parser=this.parser;const json=parser.json;const textureDef=json.textures[textureIndex];if(!textureDef.extensions||!textureDef.extensions[name]){return null}const extension=textureDef.extensions[name];const source=json.images[extension.source];let loader=parser.textureLoader;if(source.uri){const handler=parser.options.manager.getHandler(source.uri);if(handler!==null)loader=handler}return this.detectSupport().then((function(isSupported){if(isSupported)return parser.loadTextureImage(textureIndex,extension.source,loader);if(json.extensionsRequired&&json.extensionsRequired.indexOf(name)>=0){throw new Error("THREE.GLTFLoader: WebP required by asset but unsupported.")}return parser.loadTexture(textureIndex)}))}detectSupport(){if(!this.isSupported){this.isSupported=new Promise((function(resolve){const image=new Image;image.src="data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA";image.onload=image.onerror=function(){resolve(image.height===1)}}))}return this.isSupported}}class GLTFMeshoptCompression{constructor(parser){this.name=EXTENSIONS$1.EXT_MESHOPT_COMPRESSION;this.parser=parser}loadBufferView(index){const json=this.parser.json;const bufferView=json.bufferViews[index];if(bufferView.extensions&&bufferView.extensions[this.name]){const extensionDef=bufferView.extensions[this.name];const buffer=this.parser.getDependency("buffer",extensionDef.buffer);const decoder=this.parser.options.meshoptDecoder;if(!decoder||!decoder.supported){if(json.extensionsRequired&&json.extensionsRequired.indexOf(this.name)>=0){throw new Error("THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files")}else{return null}}return Promise.all([buffer,decoder.ready]).then((function(res){const byteOffset=extensionDef.byteOffset||0;const byteLength=extensionDef.byteLength||0;const count=extensionDef.count;const stride=extensionDef.byteStride;const result=new ArrayBuffer(count*stride);const source=new Uint8Array(res[0],byteOffset,byteLength);decoder.decodeGltfBuffer(new Uint8Array(result),count,stride,source,extensionDef.mode,extensionDef.filter);return result}))}else{return null}}}const BINARY_EXTENSION_HEADER_MAGIC="glTF";const BINARY_EXTENSION_HEADER_LENGTH$1=12;const BINARY_EXTENSION_CHUNK_TYPES={JSON:1313821514,BIN:5130562};class GLTFBinaryExtension$1{constructor(data){this.name=EXTENSIONS$1.KHR_BINARY_GLTF;this.content=null;this.body=null;const headerView=new DataView(data,0,BINARY_EXTENSION_HEADER_LENGTH$1);this.header={magic:LoaderUtils.decodeText(new Uint8Array(data.slice(0,4))),version:headerView.getUint32(4,true),length:headerView.getUint32(8,true)};if(this.header.magic!==BINARY_EXTENSION_HEADER_MAGIC){throw new Error("THREE.GLTFLoader: Unsupported glTF-Binary header.")}else if(this.header.version<2){throw new Error("THREE.GLTFLoader: Legacy binary file detected.")}const chunkContentsLength=this.header.length-BINARY_EXTENSION_HEADER_LENGTH$1;const chunkView=new DataView(data,BINARY_EXTENSION_HEADER_LENGTH$1);let chunkIndex=0;while(chunkIndex",specularMapParsFragmentChunk).replace("#include ",glossinessMapParsFragmentChunk).replace("#include ",specularMapFragmentChunk).replace("#include ",glossinessMapFragmentChunk).replace("#include ",lightPhysicalFragmentChunk)};Object.defineProperties(this,{specular:{get:function(){return uniforms.specular.value},set:function(v){uniforms.specular.value=v}},specularMap:{get:function(){return uniforms.specularMap.value},set:function(v){uniforms.specularMap.value=v;if(v){this.defines.USE_SPECULARMAP=""}else{delete this.defines.USE_SPECULARMAP}}},glossiness:{get:function(){return uniforms.glossiness.value},set:function(v){uniforms.glossiness.value=v}},glossinessMap:{get:function(){return uniforms.glossinessMap.value},set:function(v){uniforms.glossinessMap.value=v;if(v){this.defines.USE_GLOSSINESSMAP="";this.defines.USE_UV=""}else{delete this.defines.USE_GLOSSINESSMAP;delete this.defines.USE_UV}}}});delete this.metalness;delete this.roughness;delete this.metalnessMap;delete this.roughnessMap;this.setValues(params)}copy(source){super.copy(source);this.specularMap=source.specularMap;this.specular.copy(source.specular);this.glossinessMap=source.glossinessMap;this.glossiness=source.glossiness;delete this.metalness;delete this.roughness;delete this.metalnessMap;delete this.roughnessMap;return this}}class GLTFMaterialsPbrSpecularGlossinessExtension{constructor(){this.name=EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS;this.specularGlossinessParams=["color","map","lightMap","lightMapIntensity","aoMap","aoMapIntensity","emissive","emissiveIntensity","emissiveMap","bumpMap","bumpScale","normalMap","normalMapType","displacementMap","displacementScale","displacementBias","specularMap","specular","glossinessMap","glossiness","alphaMap","envMap","envMapIntensity"]}getMaterialType(){return GLTFMeshStandardSGMaterial}extendParams(materialParams,materialDef,parser){const pbrSpecularGlossiness=materialDef.extensions[this.name];materialParams.color=new Color(1,1,1);materialParams.opacity=1;const pending=[];if(Array.isArray(pbrSpecularGlossiness.diffuseFactor)){const array=pbrSpecularGlossiness.diffuseFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3]}if(pbrSpecularGlossiness.diffuseTexture!==undefined){pending.push(parser.assignTexture(materialParams,"map",pbrSpecularGlossiness.diffuseTexture,sRGBEncoding))}materialParams.emissive=new Color(0,0,0);materialParams.glossiness=pbrSpecularGlossiness.glossinessFactor!==undefined?pbrSpecularGlossiness.glossinessFactor:1;materialParams.specular=new Color(1,1,1);if(Array.isArray(pbrSpecularGlossiness.specularFactor)){materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor)}if(pbrSpecularGlossiness.specularGlossinessTexture!==undefined){const specGlossMapDef=pbrSpecularGlossiness.specularGlossinessTexture;pending.push(parser.assignTexture(materialParams,"glossinessMap",specGlossMapDef));pending.push(parser.assignTexture(materialParams,"specularMap",specGlossMapDef,sRGBEncoding))}return Promise.all(pending)}createMaterial(materialParams){const material=new GLTFMeshStandardSGMaterial(materialParams);material.fog=true;material.color=materialParams.color;material.map=materialParams.map===undefined?null:materialParams.map;material.lightMap=null;material.lightMapIntensity=1;material.aoMap=materialParams.aoMap===undefined?null:materialParams.aoMap;material.aoMapIntensity=1;material.emissive=materialParams.emissive;material.emissiveIntensity=1;material.emissiveMap=materialParams.emissiveMap===undefined?null:materialParams.emissiveMap;material.bumpMap=materialParams.bumpMap===undefined?null:materialParams.bumpMap;material.bumpScale=1;material.normalMap=materialParams.normalMap===undefined?null:materialParams.normalMap;material.normalMapType=TangentSpaceNormalMap;if(materialParams.normalScale)material.normalScale=materialParams.normalScale;material.displacementMap=null;material.displacementScale=1;material.displacementBias=0;material.specularMap=materialParams.specularMap===undefined?null:materialParams.specularMap;material.specular=materialParams.specular;material.glossinessMap=materialParams.glossinessMap===undefined?null:materialParams.glossinessMap;material.glossiness=materialParams.glossiness;material.alphaMap=null;material.envMap=materialParams.envMap===undefined?null:materialParams.envMap;material.envMapIntensity=1;return material}}class GLTFMeshQuantizationExtension{constructor(){this.name=EXTENSIONS$1.KHR_MESH_QUANTIZATION}}class GLTFCubicSplineInterpolant extends Interpolant{constructor(parameterPositions,sampleValues,sampleSize,resultBuffer){super(parameterPositions,sampleValues,sampleSize,resultBuffer)}copySampleValue_(index){const result=this.resultBuffer,values=this.sampleValues,valueSize=this.valueSize,offset=index*valueSize*3+valueSize;for(let i=0;i!==valueSize;i++){result[i]=values[offset+i]}return result}}GLTFCubicSplineInterpolant.prototype.beforeStart_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.afterEnd_=GLTFCubicSplineInterpolant.prototype.copySampleValue_;GLTFCubicSplineInterpolant.prototype.interpolate_=function(i1,t0,t,t1){const result=this.resultBuffer;const values=this.sampleValues;const stride=this.valueSize;const stride2=stride*2;const stride3=stride*3;const td=t1-t0;const p=(t-t0)/td;const pp=p*p;const ppp=pp*p;const offset1=i1*stride3;const offset0=offset1-stride3;const s2=-2*ppp+3*pp;const s3=ppp-pp;const s0=1-s2;const s1=s3-pp+p;for(let i=0;i!==stride;i++){const p0=values[offset0+i+stride];const m0=values[offset0+i+stride2]*td;const p1=values[offset1+i+stride];const m1=values[offset1+i]*td;result[i]=s0*p0+s1*m0+s2*p1+s3*m1}return result};const _q=new Quaternion;class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant{interpolate_(i1,t0,t,t1){const result=super.interpolate_(i1,t0,t,t1);_q.fromArray(result).normalize().toArray(result);return result}}const WEBGL_CONSTANTS$1={FLOAT:5126,FLOAT_MAT3:35675,FLOAT_MAT4:35676,FLOAT_VEC2:35664,FLOAT_VEC3:35665,FLOAT_VEC4:35666,LINEAR:9729,REPEAT:10497,SAMPLER_2D:35678,POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6,UNSIGNED_BYTE:5121,UNSIGNED_SHORT:5123};const WEBGL_COMPONENT_TYPES$1={5120:Int8Array,5121:Uint8Array,5122:Int16Array,5123:Uint16Array,5125:Uint32Array,5126:Float32Array};const WEBGL_FILTERS$1={9728:NearestFilter,9729:LinearFilter,9984:NearestMipmapNearestFilter,9985:LinearMipmapNearestFilter,9986:NearestMipmapLinearFilter,9987:LinearMipmapLinearFilter};const WEBGL_WRAPPINGS$1={33071:ClampToEdgeWrapping,33648:MirroredRepeatWrapping,10497:RepeatWrapping};const WEBGL_TYPE_SIZES$1={SCALAR:1,VEC2:2,VEC3:3,VEC4:4,MAT2:4,MAT3:9,MAT4:16};const ATTRIBUTES={POSITION:"position",NORMAL:"normal",TANGENT:"tangent",TEXCOORD_0:"uv",TEXCOORD_1:"uv2",COLOR_0:"color",WEIGHTS_0:"skinWeight",JOINTS_0:"skinIndex"};const PATH_PROPERTIES$1={scale:"scale",translation:"position",rotation:"quaternion",weights:"morphTargetInfluences"};const INTERPOLATION$1={CUBICSPLINE:undefined,LINEAR:InterpolateLinear,STEP:InterpolateDiscrete};const ALPHA_MODES={OPAQUE:"OPAQUE",MASK:"MASK",BLEND:"BLEND"};function createDefaultMaterial$1(cache){if(cache["DefaultMaterial"]===undefined){cache["DefaultMaterial"]=new MeshStandardMaterial({color:16777215,emissive:0,metalness:1,roughness:1,transparent:false,depthTest:true,side:FrontSide})}return cache["DefaultMaterial"]}function addUnknownExtensionsToUserData(knownExtensions,object,objectDef){for(const name in objectDef.extensions){if(knownExtensions[name]===undefined){object.userData.gltfExtensions=object.userData.gltfExtensions||{};object.userData.gltfExtensions[name]=objectDef.extensions[name]}}}function assignExtrasToUserData(object,gltfDef){if(gltfDef.extras!==undefined){if(typeof gltfDef.extras==="object"){Object.assign(object.userData,gltfDef.extras)}else{console.warn("THREE.GLTFLoader: Ignoring primitive type .extras, "+gltfDef.extras)}}}function addMorphTargets(geometry,targets,parser){let hasMorphPosition=false;let hasMorphNormal=false;let hasMorphColor=false;for(let i=0,il=targets.length;i0||uri.search(/^data\:image\/jpeg/)===0)return"image/jpeg";if(uri.search(/\.webp($|\?)/i)>0||uri.search(/^data\:image\/webp/)===0)return"image/webp";return"image/png"}class GLTFParser$1{constructor(json={},options={}){this.json=json;this.extensions={};this.plugins={};this.options=options;this.cache=new GLTFRegistry$1;this.associations=new Map;this.primitiveCache={};this.meshCache={refs:{},uses:{}};this.cameraCache={refs:{},uses:{}};this.lightCache={refs:{},uses:{}};this.sourceCache={};this.textureCache={};this.nodeNamesUsed={};if(typeof createImageBitmap!=="undefined"&&/^((?!chrome|android).)*safari/i.test(navigator.userAgent)===false){this.textureLoader=new ImageBitmapLoader(this.options.manager)}else{this.textureLoader=new TextureLoader(this.options.manager)}this.textureLoader.setCrossOrigin(this.options.crossOrigin);this.textureLoader.setRequestHeader(this.options.requestHeader);this.fileLoader=new FileLoader(this.options.manager);this.fileLoader.setResponseType("arraybuffer");if(this.options.crossOrigin==="use-credentials"){this.fileLoader.setWithCredentials(true)}}setExtensions(extensions){this.extensions=extensions}setPlugins(plugins){this.plugins=plugins}parse(onLoad,onError){const parser=this;const json=this.json;const extensions=this.extensions;this.cache.removeAll();this._invokeAll((function(ext){return ext._markDefs&&ext._markDefs()}));Promise.all(this._invokeAll((function(ext){return ext.beforeRoot&&ext.beforeRoot()}))).then((function(){return Promise.all([parser.getDependencies("scene"),parser.getDependencies("animation"),parser.getDependencies("camera")])})).then((function(dependencies){const result={scene:dependencies[0][json.scene||0],scenes:dependencies[0],animations:dependencies[1],cameras:dependencies[2],asset:json.asset,parser:parser,userData:{}};addUnknownExtensionsToUserData(extensions,result,json);assignExtrasToUserData(result,json);Promise.all(parser._invokeAll((function(ext){return ext.afterRoot&&ext.afterRoot(result)}))).then((function(){onLoad(result)}))})).catch(onError)}_markDefs(){const nodeDefs=this.json.nodes||[];const skinDefs=this.json.skins||[];const meshDefs=this.json.meshes||[];for(let skinIndex=0,skinLength=skinDefs.length;skinIndex{const mappings=this.associations.get(original);if(mappings!=null){this.associations.set(clone,mappings)}for(const[i,child]of original.children.entries()){updateMappings(child,clone.children[i])}};updateMappings(object,ref);ref.name+="_instance_"+cache.uses[index]++;return ref}_invokeOne(func){const extensions=Object.values(this.plugins);extensions.push(this);for(let i=0;i=2)bufferAttribute.setY(index,sparseValues[i*itemSize+1]);if(itemSize>=3)bufferAttribute.setZ(index,sparseValues[i*itemSize+2]);if(itemSize>=4)bufferAttribute.setW(index,sparseValues[i*itemSize+3]);if(itemSize>=5)throw new Error("THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.")}}return bufferAttribute}))}loadTexture(textureIndex){const json=this.json;const options=this.options;const textureDef=json.textures[textureIndex];const sourceIndex=textureDef.source;const sourceDef=json.images[sourceIndex];let loader=this.textureLoader;if(sourceDef.uri){const handler=options.manager.getHandler(sourceDef.uri);if(handler!==null)loader=handler}return this.loadTextureImage(textureIndex,sourceIndex,loader)}loadTextureImage(textureIndex,sourceIndex,loader){const parser=this;const json=this.json;const textureDef=json.textures[textureIndex];const sourceDef=json.images[sourceIndex];const cacheKey=(sourceDef.uri||sourceDef.bufferView)+":"+textureDef.sampler;if(this.textureCache[cacheKey]){return this.textureCache[cacheKey]}const promise=this.loadImageSource(sourceIndex,loader).then((function(texture){texture.flipY=false;if(textureDef.name)texture.name=textureDef.name;const samplers=json.samplers||{};const sampler=samplers[textureDef.sampler]||{};texture.magFilter=WEBGL_FILTERS$1[sampler.magFilter]||LinearFilter;texture.minFilter=WEBGL_FILTERS$1[sampler.minFilter]||LinearMipmapLinearFilter;texture.wrapS=WEBGL_WRAPPINGS$1[sampler.wrapS]||RepeatWrapping;texture.wrapT=WEBGL_WRAPPINGS$1[sampler.wrapT]||RepeatWrapping;parser.associations.set(texture,{textures:textureIndex});return texture})).catch((function(){return null}));this.textureCache[cacheKey]=promise;return promise}loadImageSource(sourceIndex,loader){const parser=this;const json=this.json;const options=this.options;if(this.sourceCache[sourceIndex]!==undefined){return this.sourceCache[sourceIndex].then((texture=>texture.clone()))}const sourceDef=json.images[sourceIndex];const URL=self.URL||self.webkitURL;let sourceURI=sourceDef.uri||"";let isObjectURL=false;if(sourceDef.bufferView!==undefined){sourceURI=parser.getDependency("bufferView",sourceDef.bufferView).then((function(bufferView){isObjectURL=true;const blob=new Blob([bufferView],{type:sourceDef.mimeType});sourceURI=URL.createObjectURL(blob);return sourceURI}))}else if(sourceDef.uri===undefined){throw new Error("THREE.GLTFLoader: Image "+sourceIndex+" is missing URI and bufferView")}const promise=Promise.resolve(sourceURI).then((function(sourceURI){return new Promise((function(resolve,reject){let onLoad=resolve;if(loader.isImageBitmapLoader===true){onLoad=function(imageBitmap){const texture=new Texture(imageBitmap);texture.needsUpdate=true;resolve(texture)}}loader.load(LoaderUtils.resolveURL(sourceURI,options.path),onLoad,undefined,reject)}))})).then((function(texture){if(isObjectURL===true){URL.revokeObjectURL(sourceURI)}texture.userData.mimeType=sourceDef.mimeType||getImageURIMimeType(sourceDef.uri);return texture})).catch((function(error){console.error("THREE.GLTFLoader: Couldn't load texture",sourceURI);throw error}));this.sourceCache[sourceIndex]=promise;return promise}assignTexture(materialParams,mapName,mapDef,encoding){const parser=this;return this.getDependency("texture",mapDef.index).then((function(texture){if(mapDef.texCoord!==undefined&&mapDef.texCoord!=0&&!(mapName==="aoMap"&&mapDef.texCoord==1)){console.warn("THREE.GLTFLoader: Custom UV set "+mapDef.texCoord+" for texture "+mapName+" not yet supported.")}if(parser.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM]){const transform=mapDef.extensions!==undefined?mapDef.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM]:undefined;if(transform){const gltfReference=parser.associations.get(texture);texture=parser.extensions[EXTENSIONS$1.KHR_TEXTURE_TRANSFORM].extendTexture(texture,transform);parser.associations.set(texture,gltfReference)}}if(encoding!==undefined){texture.encoding=encoding}materialParams[mapName]=texture;return texture}))}assignFinalMaterial(mesh){const geometry=mesh.geometry;let material=mesh.material;const useDerivativeTangents=geometry.attributes.tangent===undefined;const useVertexColors=geometry.attributes.color!==undefined;const useFlatShading=geometry.attributes.normal===undefined;if(mesh.isPoints){const cacheKey="PointsMaterial:"+material.uuid;let pointsMaterial=this.cache.get(cacheKey);if(!pointsMaterial){pointsMaterial=new PointsMaterial;Material.prototype.copy.call(pointsMaterial,material);pointsMaterial.color.copy(material.color);pointsMaterial.map=material.map;pointsMaterial.sizeAttenuation=false;this.cache.add(cacheKey,pointsMaterial)}material=pointsMaterial}else if(mesh.isLine){const cacheKey="LineBasicMaterial:"+material.uuid;let lineMaterial=this.cache.get(cacheKey);if(!lineMaterial){lineMaterial=new LineBasicMaterial;Material.prototype.copy.call(lineMaterial,material);lineMaterial.color.copy(material.color);this.cache.add(cacheKey,lineMaterial)}material=lineMaterial}if(useDerivativeTangents||useVertexColors||useFlatShading){let cacheKey="ClonedMaterial:"+material.uuid+":";if(material.isGLTFSpecularGlossinessMaterial)cacheKey+="specular-glossiness:";if(useDerivativeTangents)cacheKey+="derivative-tangents:";if(useVertexColors)cacheKey+="vertex-colors:";if(useFlatShading)cacheKey+="flat-shading:";let cachedMaterial=this.cache.get(cacheKey);if(!cachedMaterial){cachedMaterial=material.clone();if(useVertexColors)cachedMaterial.vertexColors=true;if(useFlatShading)cachedMaterial.flatShading=true;if(useDerivativeTangents){if(cachedMaterial.normalScale)cachedMaterial.normalScale.y*=-1;if(cachedMaterial.clearcoatNormalScale)cachedMaterial.clearcoatNormalScale.y*=-1}this.cache.add(cacheKey,cachedMaterial);this.associations.set(cachedMaterial,this.associations.get(material))}material=cachedMaterial}if(material.aoMap&&geometry.attributes.uv2===undefined&&geometry.attributes.uv!==undefined){geometry.setAttribute("uv2",geometry.attributes.uv)}mesh.material=material}getMaterialType(){return MeshStandardMaterial}loadMaterial(materialIndex){const parser=this;const json=this.json;const extensions=this.extensions;const materialDef=json.materials[materialIndex];let materialType;const materialParams={};const materialExtensions=materialDef.extensions||{};const pending=[];if(materialExtensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]){const sgExtension=extensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];materialType=sgExtension.getMaterialType();pending.push(sgExtension.extendParams(materialParams,materialDef,parser))}else if(materialExtensions[EXTENSIONS$1.KHR_MATERIALS_UNLIT]){const kmuExtension=extensions[EXTENSIONS$1.KHR_MATERIALS_UNLIT];materialType=kmuExtension.getMaterialType();pending.push(kmuExtension.extendParams(materialParams,materialDef,parser))}else{const metallicRoughness=materialDef.pbrMetallicRoughness||{};materialParams.color=new Color(1,1,1);materialParams.opacity=1;if(Array.isArray(metallicRoughness.baseColorFactor)){const array=metallicRoughness.baseColorFactor;materialParams.color.fromArray(array);materialParams.opacity=array[3]}if(metallicRoughness.baseColorTexture!==undefined){pending.push(parser.assignTexture(materialParams,"map",metallicRoughness.baseColorTexture,sRGBEncoding))}materialParams.metalness=metallicRoughness.metallicFactor!==undefined?metallicRoughness.metallicFactor:1;materialParams.roughness=metallicRoughness.roughnessFactor!==undefined?metallicRoughness.roughnessFactor:1;if(metallicRoughness.metallicRoughnessTexture!==undefined){pending.push(parser.assignTexture(materialParams,"metalnessMap",metallicRoughness.metallicRoughnessTexture));pending.push(parser.assignTexture(materialParams,"roughnessMap",metallicRoughness.metallicRoughnessTexture))}materialType=this._invokeOne((function(ext){return ext.getMaterialType&&ext.getMaterialType(materialIndex)}));pending.push(Promise.all(this._invokeAll((function(ext){return ext.extendMaterialParams&&ext.extendMaterialParams(materialIndex,materialParams)}))))}if(materialDef.doubleSided===true){materialParams.side=DoubleSide}const alphaMode=materialDef.alphaMode||ALPHA_MODES.OPAQUE;if(alphaMode===ALPHA_MODES.BLEND){materialParams.transparent=true;materialParams.depthWrite=false}else{materialParams.transparent=false;if(alphaMode===ALPHA_MODES.MASK){materialParams.alphaTest=materialDef.alphaCutoff!==undefined?materialDef.alphaCutoff:.5}}if(materialDef.normalTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"normalMap",materialDef.normalTexture));materialParams.normalScale=new Vector2(1,1);if(materialDef.normalTexture.scale!==undefined){const scale=materialDef.normalTexture.scale;materialParams.normalScale.set(scale,scale)}}if(materialDef.occlusionTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"aoMap",materialDef.occlusionTexture));if(materialDef.occlusionTexture.strength!==undefined){materialParams.aoMapIntensity=materialDef.occlusionTexture.strength}}if(materialDef.emissiveFactor!==undefined&&materialType!==MeshBasicMaterial){materialParams.emissive=(new Color).fromArray(materialDef.emissiveFactor)}if(materialDef.emissiveTexture!==undefined&&materialType!==MeshBasicMaterial){pending.push(parser.assignTexture(materialParams,"emissiveMap",materialDef.emissiveTexture,sRGBEncoding))}return Promise.all(pending).then((function(){let material;if(materialType===GLTFMeshStandardSGMaterial){material=extensions[EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams)}else{material=new materialType(materialParams)}if(materialDef.name)material.name=materialDef.name;assignExtrasToUserData(material,materialDef);parser.associations.set(material,{materials:materialIndex});if(materialDef.extensions)addUnknownExtensionsToUserData(extensions,material,materialDef);return material}))}createUniqueName(originalName){const sanitizedName=PropertyBinding.sanitizeNodeName(originalName||"");let name=sanitizedName;for(let i=1;this.nodeNamesUsed[name];++i){name=sanitizedName+"_"+i}this.nodeNamesUsed[name]=true;return name}loadGeometries(primitives){const parser=this;const extensions=this.extensions;const cache=this.primitiveCache;function createDracoPrimitive(primitive){return extensions[EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION].decodePrimitive(primitive,parser).then((function(geometry){return addPrimitiveAttributes(geometry,primitive,parser)}))}const pending=[];for(let i=0,il=primitives.length;i0){updateMorphTargets(mesh,meshDef)}mesh.name=parser.createUniqueName(meshDef.name||"mesh_"+meshIndex);assignExtrasToUserData(mesh,meshDef);if(primitive.extensions)addUnknownExtensionsToUserData(extensions,mesh,primitive);parser.assignFinalMaterial(mesh);meshes.push(mesh)}for(let i=0,il=meshes.length;i1){node=new Group}else if(objects.length===1){node=objects[0]}else{node=new Object3D}if(node!==objects[0]){for(let i=0,il=objects.length;i{const reducedAssociations=new Map;for(const[key,value]of parser.associations){if(key instanceof Material||key instanceof Texture){reducedAssociations.set(key,value)}}node.traverse((node=>{const mappings=parser.associations.get(node);if(mappings!=null){reducedAssociations.set(node,mappings)}}));return reducedAssociations};parser.associations=reduceAssociations(scene);return scene}))}}function buildNodeHierarchy(nodeId,parentObject,json,parser){const nodeDef=json.nodes[nodeId];return parser.getDependency("node",nodeId).then((function(node){if(nodeDef.skin===undefined)return node;let skinEntry;return parser.getDependency("skin",nodeDef.skin).then((function(skin){skinEntry=skin;const pendingJoints=[];for(let i=0,il=skinEntry.joints.length;i{VRButton.xrSessionIsGranted=true}))}}}VRButton.registerSessionGrantedListener(); /*! * three-icosa * https://github.com/icosa-gallery/three-icosa * Copyright (c) 2021-2022 Icosa Gallery * Released under the Apache 2.0 Licence. - */class TiltShaderLoader extends Loader{constructor(manager){super(manager);this.loadedMaterials={}}async load(brushName,onLoad,onProgress,onError){const scope=this;const isAlreadyLoaded=this.loadedMaterials[brushName];if(isAlreadyLoaded!==undefined){onLoad(scope.parse(isAlreadyLoaded));return}const loader=new FileLoader(this.manager);loader.setPath(this.path);loader.setResponseType("text");loader.setWithCredentials(this.withCredentials);const textureLoader=new TextureLoader(this.manager);textureLoader.setPath(this.path);textureLoader.setWithCredentials(this.withCredentials);const materialParams=tiltBrushMaterialParams[brushName];materialParams.vertexShader=await loader.loadAsync(materialParams.vertexShader);materialParams.fragmentShader=await loader.loadAsync(materialParams.fragmentShader);if(materialParams.uniforms.u_MainTex){const mainTex=await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value);mainTex.name=`${brushName}_MainTex`;mainTex.wrapS=RepeatWrapping;mainTex.wrapT=RepeatWrapping;mainTex.flipY=false;materialParams.uniforms.u_MainTex.value=mainTex}if(materialParams.uniforms.u_BumpMap){const bumpMap=await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value);bumpMap.name=`${brushName}_BumpMap`;bumpMap.wrapS=RepeatWrapping;bumpMap.wrapT=RepeatWrapping;bumpMap.flipY=false;materialParams.uniforms.u_BumpMap.value=bumpMap}if(materialParams.uniforms.u_AlphaMask){const alphaMask=await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value);alphaMask.name=`${brushName}_AlphaMask`;alphaMask.wrapS=RepeatWrapping;alphaMask.wrapT=RepeatWrapping;alphaMask.flipY=false;materialParams.uniforms.u_AlphaMask.value=alphaMask}for(var lightType in UniformsLib.lights){materialParams.uniforms[lightType]=UniformsLib.lights[lightType]}let rawMaterial=new RawShaderMaterial(materialParams);this.loadedMaterials[brushName]=rawMaterial;onLoad(scope.parse(rawMaterial))}parse(rawMaterial){return rawMaterial}lookupMaterial(nameOrGuid){const name=this.lookupMaterialName(nameOrGuid);return tiltBrushMaterialParams[name]}lookupMaterialName(nameOrGuid){switch(nameOrGuid){case"BlocksBasic:":case"0e87b49c-6546-3a34-3a44-8a556d7d6c3e":return"BlocksBasic";case"BlocksGem":case"232998f8-d357-47a2-993a-53415df9be10":return"BlocksGem";case"BlocksGlass":case"3d813d82-5839-4450-8ddc-8e889ecd96c7":return"BlocksGlass";case"Bubbles":case"89d104cd-d012-426b-b5b3-bbaee63ac43c":return"Bubbles";case"CelVinyl":case"700f3aa8-9a7c-2384-8b8a-ea028905dd8c":return"CelVinyl";case"ChromaticWave":case"0f0ff7b2-a677-45eb-a7d6-0cd7206f4816":return"ChromaticWave";case"CoarseBristles":case"1161af82-50cf-47db-9706-0c3576d43c43":case"79168f10-6961-464a-8be1-57ed364c5600":return"CoarseBristles";case"Comet":case"1caa6d7d-f015-3f54-3a4b-8b5354d39f81":return"Comet";case"DiamondHull":case"c8313697-2563-47fc-832e-290f4c04b901":return"DiamondHull";case"Disco":case"4391aaaa-df73-4396-9e33-31e4e4930b27":return"Disco";case"DotMarker":case"d1d991f2-e7a0-4cf1-b328-f57e915e6260":return"DotMarker";case"Dots":case"6a1cf9f9-032c-45ec-9b1d-a6680bee30f7":return"Dots";case"DoubleTaperedFlat":case"0d3889f3-3ede-470c-8af4-f44813306126":return"DoubleTaperedFlat";case"DoubleTaperedMarker":case"0d3889f3-3ede-470c-8af4-de4813306126":return"DoubleTaperedMarker";case"DuctTape":case"d0262945-853c-4481-9cbd-88586bed93cb":case"3ca16e2f-bdcd-4da2-8631-dcef342f40f1":return"DuctTape";case"Electricity":case"f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51":return"Electricity";case"Embers":case"02ffb866-7fb2-4d15-b761-1012cefb1360":return"Embers";case"EnvironmentDiffuse":case"0ad58bbd-42bc-484e-ad9a-b61036ff4ce7":return"EnvironmentDiffuse";case"EnvironmentDiffuseLightMap":case"d01d9d6c-9a61-4aba-8146-5891fafb013b":return"EnvironmentDiffuseLightMap";case"Fire":case"cb92b597-94ca-4255-b017-0e3f42f12f9e":return"Fire";case"2d35bcf0-e4d8-452c-97b1-3311be063130":case"280c0a7a-aad8-416c-a7d2-df63d129ca70":case"55303bc4-c749-4a72-98d9-d23e68e76e18":case"Flat":return"Flat";case"cf019139-d41c-4eb0-a1d0-5cf54b0a42f3":case"geometry_Highlighter":return"Highlighter";case"Hypercolor":case"dce872c2-7b49-4684-b59b-c45387949c5c":case"e8ef32b1-baa8-460a-9c2c-9cf8506794f5":return"Hypercolor";case"HyperGrid":case"6a1cf9f9-032c-45ec-9b6e-a6680bee32e9":return"HyperGrid";case"Icing":case"2f212815-f4d3-c1a4-681a-feeaf9c6dc37":return"Icing";case"Ink":case"f5c336cf-5108-4b40-ade9-c687504385ab":case"c0012095-3ffd-4040-8ee1-fc180d346eaa":return"Ink";case"Leaves":case"4a76a27a-44d8-4bfe-9a8c-713749a499b0":case"ea19de07-d0c0-4484-9198-18489a3c1487":return"Leaves";case"Light":case"2241cd32-8ba2-48a5-9ee7-2caef7e9ed62":return"Light";case"LightWire":case"4391aaaa-df81-4396-9e33-31e4e4930b27":return"LightWire";case"Lofted":case"d381e0f5-3def-4a0d-8853-31e9200bcbda":return"Lofted";case"Marker":case"429ed64a-4e97-4466-84d3-145a861ef684":return"Marker";case"MatteHull":case"79348357-432d-4746-8e29-0e25c112e3aa":return"MatteHull";case"NeonPulse":case"b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6":return"NeonPulse";case"OilPaint":case"f72ec0e7-a844-4e38-82e3-140c44772699":case"c515dad7-4393-4681-81ad-162ef052241b":return"OilPaint";case"Paper":case"f1114e2e-eb8d-4fde-915a-6e653b54e9f5":case"759f1ebd-20cd-4720-8d41-234e0da63716":return"Paper";case"PbrTemplate":case"f86a096c-2f4f-4f9d-ae19-81b99f2944e0":return"PbrTemplate";case"PbrTransparentTemplate":case"19826f62-42ac-4a9e-8b77-4231fbd0cfbf":return"PbrTransparentTemplate";case"Petal":case"e0abbc80-0f80-e854-4970-8924a0863dcc":return"Petal";case"Plasma":case"c33714d1-b2f9-412e-bd50-1884c9d46336":return"Plasma";case"Rainbow":case"ad1ad437-76e2-450d-a23a-e17f8310b960":return"Rainbow";case"ShinyHull":case"faaa4d44-fcfb-4177-96be-753ac0421ba3":return"ShinyHull";case"Smoke":case"70d79cca-b159-4f35-990c-f02193947fe8":return"Smoke";case"Snow":case"d902ed8b-d0d1-476c-a8de-878a79e3a34c":return"Snow";case"SoftHighlighter":case"accb32f5-4509-454f-93f8-1df3fd31df1b":return"SoftHighlighter";case"Spikes":case"cf7f0059-7aeb-53a4-2b67-c83d863a9ffa":return"Spikes";case"Splatter":case"8dc4a70c-d558-4efd-a5ed-d4e860f40dc3":case"7a1c8107-50c5-4b70-9a39-421576d6617e":return"Splatter";case"Stars":case"0eb4db27-3f82-408d-b5a1-19ebd7d5b711":return"Stars";case"Streamers":case"44bb800a-fbc3-4592-8426-94ecb05ddec3":return"Streamers";case"Taffy":case"0077f88c-d93a-42f3-b59b-b31c50cdb414":return"Taffy";case"TaperedFlat":case"b468c1fb-f254-41ed-8ec9-57030bc5660c":case"c8ccb53d-ae13-45ef-8afb-b730d81394eb":return"TaperedFlat";case"TaperedMarker":case"d90c6ad8-af0f-4b54-b422-e0f92abe1b3c":case"1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0":return"TaperedMarker";case"ThickPaint":case"75b32cf0-fdd6-4d89-a64b-e2a00b247b0f":case"fdf0326a-c0d1-4fed-b101-9db0ff6d071f":return"ThickPaint";case"Toon":case"4391385a-df73-4396-9e33-31e4e4930b27":return"Toon";case"UnlitHull":case"a8fea537-da7c-4d4b-817f-24f074725d6d":return"UnlitHull";case"VelvetInk":case"d229d335-c334-495a-a801-660ac8a87360":return"VelvetInk";case"Waveform":case"10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab":return"Waveform";case"WetPaint":case"b67c0e81-ce6d-40a8-aeb0-ef036b081aa3":case"dea67637-cd1a-27e4-c9b1-52f4bbcb84e5":return"WetPaint";case"WigglyGraphite":case"5347acf0-a8e2-47b6-8346-30c70719d763":case"e814fef1-97fd-7194-4a2f-50c2bb918be2":return"WigglyGraphite";case"wire":case"4391385a-cf83-4396-9e33-31e4e4930b27":return"Wire"}}}const tiltBrushMaterialParams={BlocksBasic:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.2},u_SpecColor:{value:new Vector3(.1960784,.1960784,.1960784)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl",fragmentShader:"BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},BlocksGem:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Color:{value:new Vector4(1,1,1,1)},u_Shininess:{value:.9},u_RimIntensity:{value:.5},u_RimPower:{value:2},u_Frequency:{value:2},u_Jitter:{value:1},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl",fragmentShader:"BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},BlocksGlass:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Color:{value:new Vector4(1,1,1,1)},u_Shininess:{value:.8},u_RimIntensity:{value:.7},u_RimPower:{value:4},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl",fragmentShader:"BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Bubbles:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png"}},vertexShader:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl",fragmentShader:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},CelVinyl:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_Cutoff:{value:.554},u_MainTex:{value:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl",fragmentShader:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},ChromaticWave:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_time:{value:new Vector4},u_EmissionGain:{value:.45}},vertexShader:"ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl",fragmentShader:"ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},CoarseBristles:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png"},u_Cutoff:{value:.25},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl",fragmentShader:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Comet:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png"},u_AlphaMask:{value:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png"},u_AlphaMask_TexelSize:{value:new Vector4(.0156,1,64,1)},u_time:{value:new Vector4},u_Speed:{value:1},u_EmissionGain:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl",fragmentShader:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},DiamondHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png"},u_time:{value:new Vector4},cameraPosition:{value:new Vector3},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl",fragmentShader:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Disco:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.65},u_SpecColor:{value:new Vector3(.5147059,.5147059,.5147059)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DotMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-vertex.glsl",fragmentShader:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Dots:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-MainTex.png"},u_TintColor:{value:new Vector4(1,1,1,1)},u_EmissionGain:{value:300},u_BaseGain:{value:.4}},vertexShader:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-vertex.glsl",fragmentShader:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},DoubleTaperedFlat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.15},u_SpecColor:{value:new Vector3(0,0,0)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-vertex.glsl",fragmentShader:"DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DoubleTaperedMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-vertex.glsl",fragmentShader:"DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DuctTape:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.5372549,.5372549,.5372549)},u_Shininess:{value:.414},u_MainTex:{value:"DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-MainTex.png"},u_Cutoff:{value:.2},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-vertex.glsl",fragmentShader:"DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Electricity:{uniforms:{u_time:{value:new Vector4},u_DisplacementIntensity:{value:2},u_EmissionGain:{value:.2}},vertexShader:"Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-vertex.glsl",fragmentShader:"Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Embers:{uniforms:{u_time:{value:new Vector4},u_ScrollRate:{value:.6},u_ScrollDistance:{value:new Vector3(-.2,.6,0)},u_ScrollJitterIntensity:{value:.03},u_ScrollJitterFrequency:{value:5},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-MainTex.png"},u_Cutoff:{value:.2}},vertexShader:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-vertex.glsl",fragmentShader:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},EnvironmentDiffuse:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-vertex.glsl",fragmentShader:"EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},EnvironmentDiffuseLightMap:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-vertex.glsl",fragmentShader:"EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Fire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-MainTex.png"},u_time:{value:new Vector4},u_EmissionGain:{value:.5}},vertexShader:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-vertex.glsl",fragmentShader:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Flat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-vertex.glsl",fragmentShader:"Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Highlighter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-MainTex.png"},u_Cutoff:{value:.12}},vertexShader:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-vertex.glsl",fragmentShader:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Hypercolor:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.5},u_SpecColor:{value:new Vector3(.2745098,.2745098,.2745098)},u_MainTex:{value:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-MainTex.png"},u_time:{value:new Vector4},u_Cutoff:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-vertex.glsl",fragmentShader:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},HyperGrid:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-MainTex.png"}},vertexShader:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-vertex.glsl",fragmentShader:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Icing:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.15},u_Cutoff:{value:.5},u_MainTex:{value:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-vertex.glsl",fragmentShader:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Ink:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-vertex.glsl",fragmentShader:"Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Leaves:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.395},u_Cutoff:{value:.5},u_MainTex:{value:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-vertex.glsl",fragmentShader:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Light:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-MainTex.png"},u_EmissionGain:{value:.45}},vertexShader:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-vertex.glsl",fragmentShader:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},LightWire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.81},u_SpecColor:{value:new Vector3(.3455882,.3455882,.3455882)},u_time:{value:new Vector4},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_MainTex:{value:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-MainTex.png"}},vertexShader:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Lofted:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-vertex.glsl",fragmentShader:"Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Marker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-MainTex.png"},u_Cutoff:{value:.067},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-vertex.glsl",fragmentShader:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},MatteHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-vertex.glsl",fragmentShader:"MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},NeonPulse:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_time:{value:new Vector4},u_EmissionGain:{value:.5}},vertexShader:"NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-vertex.glsl",fragmentShader:"NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},OilPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.002,.002,512,512)}},vertexShader:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-vertex.glsl",fragmentShader:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Paper:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.145},u_Cutoff:{value:.16},u_MainTex:{value:"Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-vertex.glsl",fragmentShader:"Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},PbrTemplate:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-vertex.glsl",fragmentShader:"PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},PbrTransparentTemplate:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-vertex.glsl",fragmentShader:"PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Petal:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.01},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-vertex.glsl",fragmentShader:"Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Plasma:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-MainTex.png"},u_time:{value:new Vector4}},vertexShader:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-vertex.glsl",fragmentShader:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Rainbow:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_EmissionGain:{value:.65}},vertexShader:"Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-vertex.glsl",fragmentShader:"Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},ShinyHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.1985294,.1985294,.1985294)},u_Shininess:{value:.743},u_Cutoff:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-vertex.glsl",fragmentShader:"ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Smoke:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-MainTex.png"}},vertexShader:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-vertex.glsl",fragmentShader:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Snow:{uniforms:{u_time:{value:new Vector4},u_ScrollRate:{value:.2},u_ScrollDistance:{value:new Vector3(0,-.3,0)},u_ScrollJitterIntensity:{value:.01},u_ScrollJitterFrequency:{value:12},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-MainTex.png"}},vertexShader:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-vertex.glsl",fragmentShader:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},SoftHighlighter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-MainTex.png"}},vertexShader:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-vertex.glsl",fragmentShader:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Spikes:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-vertex.glsl",fragmentShader:"Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Splatter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-MainTex.png"},u_Cutoff:{value:.2},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-vertex.glsl",fragmentShader:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Stars:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_SparkleRate:{value:5.3},u_MainTex:{value:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-MainTex.png"}},vertexShader:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-vertex.glsl",fragmentShader:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Streamers:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-MainTex.png"},u_EmissionGain:{value:.4},u_time:{value:new Vector4}},vertexShader:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-vertex.glsl",fragmentShader:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Taffy:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-vertex.glsl",fragmentShader:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedFlat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-MainTex.png"},u_Cutoff:{value:.067},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-vertex.glsl",fragmentShader:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-vertex.glsl",fragmentShader:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedMarker_Flat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_MainTex:{value:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-MainTex.png"},u_Cutoff:{value:.2}},vertexShader:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-vertex.glsl",fragmentShader:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},ThickPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-vertex.glsl",fragmentShader:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Toon:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},UnlitHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-vertex.glsl",fragmentShader:"UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},VelvetInk:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-vertex.glsl",fragmentShader:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Waveform:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_EmissionGain:{value:.5178571},u_time:{value:new Vector4},u_MainTex:{value:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-MainTex.png"}},vertexShader:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-vertex.glsl",fragmentShader:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},WetPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.1397059,.1397059,.1397059)},u_Shininess:{value:.85},u_Cutoff:{value:.3},u_MainTex:{value:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-vertex.glsl",fragmentShader:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},WigglyGraphite:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Cutoff:{value:.5},u_MainTex:{value:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-vertex.glsl",fragmentShader:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Wire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0}};class GLTFGoogleTiltBrushMaterialExtension{constructor(parser,brushPath){this.name="GOOGLE_tilt_brush_material";this.parser=parser;this.brushPath=brushPath;if(this.brushPath.slice(this.brushPath.length-1)!=="/"){this.brushPath+="/"}this.tiltShaderLoader=new TiltShaderLoader(parser.options.manager);this.tiltShaderLoader.setPath(brushPath);this.clock=new Clock}beforeRoot(){const parser=this.parser;const json=parser.json;if(!json.extensionsUsed||!json.extensionsUsed.includes(this.name)){return null}json.materials.forEach((material=>{const extensionsDef=material.extensions;if(!extensionsDef||!extensionsDef[this.name]){return}const guid=material.extensions.GOOGLE_tilt_brush_material.guid;const materialParams=this.tiltShaderLoader.lookupMaterial(guid);if(material?.pbrMetallicRoughness?.baseColorTexture){const mainTex=json.images[material.pbrMetallicRoughness.baseColorTexture.index];mainTex.uri=this.brushPath+materialParams.uniforms.u_MainTex.value}if(material?.normalTexture){const bumpMap=json.images[material.normalTexture.index];bumpMap.uri=this.brushPath+materialParams.uniforms.u_BumpMap.value}}))}afterRoot(glTF){const parser=this.parser;const json=parser.json;if(!json.extensionsUsed||!json.extensionsUsed.includes(this.name)){return null}const shaderResolves=[];for(const scene of glTF.scenes){scene.traverse((async object=>{const association=parser.associations.get(object);if(association===undefined||association.type!=="nodes"){return}const node=json.nodes[association.index];if(node.mesh===undefined){return}const prim=json.meshes[node.mesh].primitives[0];const mat=json.materials[prim.material];const extensionsDef=mat.extensions;if(!extensionsDef||!extensionsDef[this.name]){return}const guid=extensionsDef.GOOGLE_tilt_brush_material.guid;shaderResolves.push(this.replaceMaterial(object,guid))}))}return Promise.all(shaderResolves)}async replaceMaterial(mesh,guid){let shader;let updateTime=false;let updateCamera=false;switch(guid){case"0e87b49c-6546-3a34-3a44-8a556d7d6c3e":mesh.geometry.name="geometry_BlocksBasic";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksBasic");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksBasic";break;case"232998f8-d357-47a2-993a-53415df9be10":mesh.geometry.name="geometry_BlocksGem";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksGem");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGem";break;case"3d813d82-5839-4450-8ddc-8e889ecd96c7":mesh.geometry.name="geometry_BlocksGlass";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksGlass");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGlass";break;case"89d104cd-d012-426b-b5b3-bbaee63ac43c":mesh.geometry.name="geometry_Bubbles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Bubbles");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Bubbles";break;case"700f3aa8-9a7c-2384-8b8a-ea028905dd8c":mesh.geometry.name="geometry_CelVinyl";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("CelVinyl");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CelVinyl";break;case"0f0ff7b2-a677-45eb-a7d6-0cd7206f4816":mesh.geometry.name="geometry_ChromaticWave";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ChromaticWave");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ChromaticWave";updateTime=true;break;case"1161af82-50cf-47db-9706-0c3576d43c43":case"79168f10-6961-464a-8be1-57ed364c5600":mesh.geometry.name="geometry_CoarseBristles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("CoarseBristles");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CoarseBristles";break;case"1caa6d7d-f015-3f54-3a4b-8b5354d39f81":mesh.geometry.name="geometry_Comet";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Comet");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Comet";updateTime=true;break;case"c8313697-2563-47fc-832e-290f4c04b901":mesh.geometry.name="geometry_DiamondHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DiamondHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";updateTime=true;updateCamera=true;break;case"4391aaaa-df73-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Disco";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Disco");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Disco";updateTime=true;break;case"d1d991f2-e7a0-4cf1-b328-f57e915e6260":mesh.geometry.name="geometry_DotMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DotMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DotMarker";break;case"6a1cf9f9-032c-45ec-9b1d-a6680bee30f7":mesh.geometry.name="geometry_Dots";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Dots");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Dots";break;case"0d3889f3-3ede-470c-8af4-f44813306126":mesh.geometry.name="geometry_DoubleTaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DoubleTaperedFlat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedFlat";break;case"0d3889f3-3ede-470c-8af4-de4813306126":mesh.geometry.name="geometry_DoubleTaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DoubleTaperedMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedMarker";break;case"d0262945-853c-4481-9cbd-88586bed93cb":case"3ca16e2f-bdcd-4da2-8631-dcef342f40f1":mesh.geometry.name="geometry_DuctTape";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DuctTape");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DuctTape";break;case"f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51":mesh.geometry.name="geometry_Electricity";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Electricity");mesh.material=shader;mesh.material.name="material_Electricity";updateTime=true;break;case"02ffb866-7fb2-4d15-b761-1012cefb1360":mesh.geometry.name="geometry_Embers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Embers");mesh.material=shader;mesh.material.name="material_Embers";updateTime=true;break;case"0ad58bbd-42bc-484e-ad9a-b61036ff4ce7":mesh.geometry.name="geometry_EnvironmentDiffuse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("EnvironmentDiffuse");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuse";break;case"d01d9d6c-9a61-4aba-8146-5891fafb013b":mesh.geometry.name="geometry_EnvironmentDiffuseLightMap";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("EnvironmentDiffuseLightMap");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuseLightMap";break;case"cb92b597-94ca-4255-b017-0e3f42f12f9e":mesh.geometry.name="geometry_Fire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Fire");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Fire";updateTime=true;break;case"2d35bcf0-e4d8-452c-97b1-3311be063130":case"280c0a7a-aad8-416c-a7d2-df63d129ca70":case"55303bc4-c749-4a72-98d9-d23e68e76e18":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Flat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"cf019139-d41c-4eb0-a1d0-5cf54b0a42f3":mesh.geometry.name="geometry_Highlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Highlighter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Highlighter";break;case"dce872c2-7b49-4684-b59b-c45387949c5c":case"e8ef32b1-baa8-460a-9c2c-9cf8506794f5":mesh.geometry.name="geometry_Hypercolor";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Hypercolor");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Hypercolor";updateTime=true;break;case"6a1cf9f9-032c-45ec-9b6e-a6680bee32e9":mesh.geometry.name="geometry_HyperGrid";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("HyperGrid");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_HyperGrid";break;case"2f212815-f4d3-c1a4-681a-feeaf9c6dc37":mesh.geometry.name="geometry_Icing";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Icing");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Icing";break;case"f5c336cf-5108-4b40-ade9-c687504385ab":case"c0012095-3ffd-4040-8ee1-fc180d346eaa":mesh.geometry.name="geometry_Ink";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Ink");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Ink";break;case"4a76a27a-44d8-4bfe-9a8c-713749a499b0":case"ea19de07-d0c0-4484-9198-18489a3c1487":mesh.geometry.name="geometry_Leaves";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Leaves");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Leaves";break;case"2241cd32-8ba2-48a5-9ee7-2caef7e9ed62":mesh.geometry.name="geometry_Light";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Light");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Light";break;case"4391aaaa-df81-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_LightWire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("LightWire");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_LightWire";updateTime=true;break;case"d381e0f5-3def-4a0d-8853-31e9200bcbda":mesh.geometry.name="geometry_Lofted";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Lofted");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Lofted";break;case"429ed64a-4e97-4466-84d3-145a861ef684":mesh.geometry.name="geometry_Marker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Marker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Marker";break;case"79348357-432d-4746-8e29-0e25c112e3aa":mesh.geometry.name="geometry_MatteHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("MatteHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_MatteHull";break;case"b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6":mesh.geometry.name="geometry_NeonPulse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("NeonPulse");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_NeonPulse";updateTime=true;break;case"f72ec0e7-a844-4e38-82e3-140c44772699":case"c515dad7-4393-4681-81ad-162ef052241b":mesh.geometry.name="geometry_OilPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("OilPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_OilPaint";break;case"f1114e2e-eb8d-4fde-915a-6e653b54e9f5":case"759f1ebd-20cd-4720-8d41-234e0da63716":mesh.geometry.name="geometry_Paper";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Paper");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Paper";break;case"f86a096c-2f4f-4f9d-ae19-81b99f2944e0":mesh.geometry.name="geometry_PbrTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("PbrTemplate");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTemplate";break;case"19826f62-42ac-4a9e-8b77-4231fbd0cfbf":mesh.geometry.name="geometry_PbrTransparentTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("PbrTransparentTemplate");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTransparentTemplate";break;case"e0abbc80-0f80-e854-4970-8924a0863dcc":mesh.geometry.name="geometry_Petal";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Petal");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Petal";break;case"c33714d1-b2f9-412e-bd50-1884c9d46336":mesh.geometry.name="geometry_Plasma";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Plasma");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Plasma";updateTime=true;break;case"ad1ad437-76e2-450d-a23a-e17f8310b960":mesh.geometry.name="geometry_Rainbow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Rainbow");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Rainbow";updateTime=true;break;case"faaa4d44-fcfb-4177-96be-753ac0421ba3":mesh.geometry.name="geometry_ShinyHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ShinyHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ShinyHull";break;case"70d79cca-b159-4f35-990c-f02193947fe8":mesh.geometry.name="geometry_Smoke";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Smoke");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Smoke";break;case"d902ed8b-d0d1-476c-a8de-878a79e3a34c":mesh.geometry.name="geometry_Snow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Snow");mesh.material=shader;mesh.material.name="material_Snow";updateTime=true;break;case"accb32f5-4509-454f-93f8-1df3fd31df1b":mesh.geometry.name="geometry_SoftHighlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("SoftHighlighter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_SoftHighlighter";break;case"cf7f0059-7aeb-53a4-2b67-c83d863a9ffa":mesh.geometry.name="geometry_Spikes";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Spikes");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Spikes";break;case"8dc4a70c-d558-4efd-a5ed-d4e860f40dc3":case"7a1c8107-50c5-4b70-9a39-421576d6617e":mesh.geometry.name="geometry_Splatter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Splatter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Splatter";break;case"0eb4db27-3f82-408d-b5a1-19ebd7d5b711":mesh.geometry.name="geometry_Stars";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Stars");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Stars";updateTime=true;break;case"44bb800a-fbc3-4592-8426-94ecb05ddec3":mesh.geometry.name="geometry_Streamers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Streamers");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Streamers";updateTime=true;break;case"0077f88c-d93a-42f3-b59b-b31c50cdb414":mesh.geometry.name="geometry_Taffy";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Taffy");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Taffy";updateTime=true;break;case"b468c1fb-f254-41ed-8ec9-57030bc5660c":case"c8ccb53d-ae13-45ef-8afb-b730d81394eb":mesh.geometry.name="geometry_TaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("TaperedFlat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedFlat";break;case"d90c6ad8-af0f-4b54-b422-e0f92abe1b3c":case"1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0":mesh.geometry.name="geometry_TaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("TaperedMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedMarker";break;case"75b32cf0-fdd6-4d89-a64b-e2a00b247b0f":case"fdf0326a-c0d1-4fed-b101-9db0ff6d071f":mesh.geometry.name="geometry_ThickPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ThickPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ThickPaint";break;case"4391385a-df73-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Toon";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("Toon");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Toon";break;case"a8fea537-da7c-4d4b-817f-24f074725d6d":mesh.geometry.name="geometry_UnlitHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("UnlitHull");mesh.material=shader;mesh.material.name="material_UnlitHull";break;case"d229d335-c334-495a-a801-660ac8a87360":mesh.geometry.name="geometry_VelvetInk";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("VelvetInk");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_VelvetInk";break;case"10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab":mesh.geometry.name="geometry_Waveform";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Waveform");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Waveform";updateTime=true;break;case"b67c0e81-ce6d-40a8-aeb0-ef036b081aa3":case"dea67637-cd1a-27e4-c9b1-52f4bbcb84e5":mesh.geometry.name="geometry_WetPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("WetPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WetPaint";break;case"5347acf0-a8e2-47b6-8346-30c70719d763":case"e814fef1-97fd-7194-4a2f-50c2bb918be2":mesh.geometry.name="geometry_WigglyGraphite";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("WigglyGraphite");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WigglyGraphite";updateTime=true;break;case"4391385a-cf83-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Wire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("Wire");mesh.material=shader;mesh.material.name="material_Wire";break;default:console.warn(`Could not find brush with guid ${guid}!`)}mesh.onBeforeRender=(renderer,scene,camera,geometry,material,group)=>{if(updateTime||updateCamera){const elapsedTime=this.clock.getElapsedTime();const time=new Vector4(elapsedTime/20,elapsedTime,elapsedTime*2,elapsedTime*3);if(updateTime){material.uniforms["u_time"].value=time}if(updateCamera){material.uniforms["cameraPosition"].value=camera.position}}if(material?.uniforms?.directionalLights?.value){if(material.uniforms.directionalLights.value[0]){if(material.uniforms.u_SceneLight_0_color){const color=material.uniforms.directionalLights.value[0].color;material.uniforms.u_SceneLight_0_color.value=new Vector4(color.r,color.g,color.b,1)}}if(material.uniforms.directionalLights.value[1]){if(material.uniforms.u_SceneLight_1_color){const color=material.uniforms.directionalLights.value[1].color;material.uniforms.u_SceneLight_1_color.value=new Vector4(color.r,color.g,color.b,1)}}}}}} + */class TiltShaderLoader$1 extends Loader{constructor(manager){super(manager);this.loadedMaterials={}}async load(brushName,onLoad,onProgress,onError){const scope=this;const isAlreadyLoaded=this.loadedMaterials[brushName];if(isAlreadyLoaded!==undefined){onLoad(scope.parse(isAlreadyLoaded));return}const loader=new FileLoader(this.manager);loader.setPath(this.path);loader.setResponseType("text");loader.setWithCredentials(this.withCredentials);const textureLoader=new TextureLoader(this.manager);textureLoader.setPath(this.path);textureLoader.setWithCredentials(this.withCredentials);const materialParams=tiltBrushMaterialParams$1[brushName];materialParams.vertexShader=await loader.loadAsync(materialParams.vertexShader);materialParams.fragmentShader=await loader.loadAsync(materialParams.fragmentShader);if(materialParams.uniforms.u_MainTex){const mainTex=await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value);mainTex.name=`${brushName}_MainTex`;mainTex.wrapS=RepeatWrapping;mainTex.wrapT=RepeatWrapping;mainTex.flipY=false;materialParams.uniforms.u_MainTex.value=mainTex}if(materialParams.uniforms.u_BumpMap){const bumpMap=await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value);bumpMap.name=`${brushName}_BumpMap`;bumpMap.wrapS=RepeatWrapping;bumpMap.wrapT=RepeatWrapping;bumpMap.flipY=false;materialParams.uniforms.u_BumpMap.value=bumpMap}if(materialParams.uniforms.u_AlphaMask){const alphaMask=await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value);alphaMask.name=`${brushName}_AlphaMask`;alphaMask.wrapS=RepeatWrapping;alphaMask.wrapT=RepeatWrapping;alphaMask.flipY=false;materialParams.uniforms.u_AlphaMask.value=alphaMask}for(var lightType in UniformsLib.lights){materialParams.uniforms[lightType]=UniformsLib.lights[lightType]}let rawMaterial=new RawShaderMaterial(materialParams);this.loadedMaterials[brushName]=rawMaterial;onLoad(scope.parse(rawMaterial))}parse(rawMaterial){return rawMaterial}lookupMaterial(nameOrGuid){const name=this.lookupMaterialName(nameOrGuid);return tiltBrushMaterialParams$1[name]}lookupMaterialName(nameOrGuid){switch(nameOrGuid){case"BlocksBasic:":case"0e87b49c-6546-3a34-3a44-8a556d7d6c3e":return"BlocksBasic";case"BlocksGem":case"232998f8-d357-47a2-993a-53415df9be10":return"BlocksGem";case"BlocksGlass":case"3d813d82-5839-4450-8ddc-8e889ecd96c7":return"BlocksGlass";case"Bubbles":case"89d104cd-d012-426b-b5b3-bbaee63ac43c":return"Bubbles";case"CelVinyl":case"700f3aa8-9a7c-2384-8b8a-ea028905dd8c":return"CelVinyl";case"ChromaticWave":case"0f0ff7b2-a677-45eb-a7d6-0cd7206f4816":return"ChromaticWave";case"CoarseBristles":case"1161af82-50cf-47db-9706-0c3576d43c43":case"79168f10-6961-464a-8be1-57ed364c5600":return"CoarseBristles";case"Comet":case"1caa6d7d-f015-3f54-3a4b-8b5354d39f81":return"Comet";case"DiamondHull":case"c8313697-2563-47fc-832e-290f4c04b901":return"DiamondHull";case"Disco":case"4391aaaa-df73-4396-9e33-31e4e4930b27":return"Disco";case"DotMarker":case"d1d991f2-e7a0-4cf1-b328-f57e915e6260":return"DotMarker";case"Dots":case"6a1cf9f9-032c-45ec-9b1d-a6680bee30f7":return"Dots";case"DoubleTaperedFlat":case"0d3889f3-3ede-470c-8af4-f44813306126":return"DoubleTaperedFlat";case"DoubleTaperedMarker":case"0d3889f3-3ede-470c-8af4-de4813306126":return"DoubleTaperedMarker";case"DuctTape":case"d0262945-853c-4481-9cbd-88586bed93cb":case"3ca16e2f-bdcd-4da2-8631-dcef342f40f1":return"DuctTape";case"Electricity":case"f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51":return"Electricity";case"Embers":case"02ffb866-7fb2-4d15-b761-1012cefb1360":return"Embers";case"EnvironmentDiffuse":case"0ad58bbd-42bc-484e-ad9a-b61036ff4ce7":return"EnvironmentDiffuse";case"EnvironmentDiffuseLightMap":case"d01d9d6c-9a61-4aba-8146-5891fafb013b":return"EnvironmentDiffuseLightMap";case"Fire":case"cb92b597-94ca-4255-b017-0e3f42f12f9e":return"Fire";case"2d35bcf0-e4d8-452c-97b1-3311be063130":case"280c0a7a-aad8-416c-a7d2-df63d129ca70":case"55303bc4-c749-4a72-98d9-d23e68e76e18":case"Flat":return"Flat";case"cf019139-d41c-4eb0-a1d0-5cf54b0a42f3":case"geometry_Highlighter":return"Highlighter";case"Hypercolor":case"dce872c2-7b49-4684-b59b-c45387949c5c":case"e8ef32b1-baa8-460a-9c2c-9cf8506794f5":return"Hypercolor";case"HyperGrid":case"6a1cf9f9-032c-45ec-9b6e-a6680bee32e9":return"HyperGrid";case"Icing":case"2f212815-f4d3-c1a4-681a-feeaf9c6dc37":return"Icing";case"Ink":case"f5c336cf-5108-4b40-ade9-c687504385ab":case"c0012095-3ffd-4040-8ee1-fc180d346eaa":return"Ink";case"Leaves":case"4a76a27a-44d8-4bfe-9a8c-713749a499b0":case"ea19de07-d0c0-4484-9198-18489a3c1487":return"Leaves";case"Light":case"2241cd32-8ba2-48a5-9ee7-2caef7e9ed62":return"Light";case"LightWire":case"4391aaaa-df81-4396-9e33-31e4e4930b27":return"LightWire";case"Lofted":case"d381e0f5-3def-4a0d-8853-31e9200bcbda":return"Lofted";case"Marker":case"429ed64a-4e97-4466-84d3-145a861ef684":return"Marker";case"MatteHull":case"79348357-432d-4746-8e29-0e25c112e3aa":return"MatteHull";case"NeonPulse":case"b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6":return"NeonPulse";case"OilPaint":case"f72ec0e7-a844-4e38-82e3-140c44772699":case"c515dad7-4393-4681-81ad-162ef052241b":return"OilPaint";case"Paper":case"f1114e2e-eb8d-4fde-915a-6e653b54e9f5":case"759f1ebd-20cd-4720-8d41-234e0da63716":return"Paper";case"PbrTemplate":case"f86a096c-2f4f-4f9d-ae19-81b99f2944e0":return"PbrTemplate";case"PbrTransparentTemplate":case"19826f62-42ac-4a9e-8b77-4231fbd0cfbf":return"PbrTransparentTemplate";case"Petal":case"e0abbc80-0f80-e854-4970-8924a0863dcc":return"Petal";case"Plasma":case"c33714d1-b2f9-412e-bd50-1884c9d46336":return"Plasma";case"Rainbow":case"ad1ad437-76e2-450d-a23a-e17f8310b960":return"Rainbow";case"ShinyHull":case"faaa4d44-fcfb-4177-96be-753ac0421ba3":return"ShinyHull";case"Smoke":case"70d79cca-b159-4f35-990c-f02193947fe8":return"Smoke";case"Snow":case"d902ed8b-d0d1-476c-a8de-878a79e3a34c":return"Snow";case"SoftHighlighter":case"accb32f5-4509-454f-93f8-1df3fd31df1b":return"SoftHighlighter";case"Spikes":case"cf7f0059-7aeb-53a4-2b67-c83d863a9ffa":return"Spikes";case"Splatter":case"8dc4a70c-d558-4efd-a5ed-d4e860f40dc3":case"7a1c8107-50c5-4b70-9a39-421576d6617e":return"Splatter";case"Stars":case"0eb4db27-3f82-408d-b5a1-19ebd7d5b711":return"Stars";case"Streamers":case"44bb800a-fbc3-4592-8426-94ecb05ddec3":return"Streamers";case"Taffy":case"0077f88c-d93a-42f3-b59b-b31c50cdb414":return"Taffy";case"TaperedFlat":case"b468c1fb-f254-41ed-8ec9-57030bc5660c":case"c8ccb53d-ae13-45ef-8afb-b730d81394eb":return"TaperedFlat";case"TaperedMarker":case"d90c6ad8-af0f-4b54-b422-e0f92abe1b3c":case"1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0":return"TaperedMarker";case"ThickPaint":case"75b32cf0-fdd6-4d89-a64b-e2a00b247b0f":case"fdf0326a-c0d1-4fed-b101-9db0ff6d071f":return"ThickPaint";case"Toon":case"4391385a-df73-4396-9e33-31e4e4930b27":return"Toon";case"UnlitHull":case"a8fea537-da7c-4d4b-817f-24f074725d6d":return"UnlitHull";case"VelvetInk":case"d229d335-c334-495a-a801-660ac8a87360":return"VelvetInk";case"Waveform":case"10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab":return"Waveform";case"WetPaint":case"b67c0e81-ce6d-40a8-aeb0-ef036b081aa3":case"dea67637-cd1a-27e4-c9b1-52f4bbcb84e5":return"WetPaint";case"WigglyGraphite":case"5347acf0-a8e2-47b6-8346-30c70719d763":case"e814fef1-97fd-7194-4a2f-50c2bb918be2":return"WigglyGraphite";case"wire":case"4391385a-cf83-4396-9e33-31e4e4930b27":return"Wire"}}}const tiltBrushMaterialParams$1={BlocksBasic:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.2},u_SpecColor:{value:new Vector3(.1960784,.1960784,.1960784)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl",fragmentShader:"BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},BlocksGem:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Color:{value:new Vector4(1,1,1,1)},u_Shininess:{value:.9},u_RimIntensity:{value:.5},u_RimPower:{value:2},u_Frequency:{value:2},u_Jitter:{value:1},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl",fragmentShader:"BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},BlocksGlass:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Color:{value:new Vector4(1,1,1,1)},u_Shininess:{value:.8},u_RimIntensity:{value:.7},u_RimPower:{value:4},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl",fragmentShader:"BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Bubbles:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png"}},vertexShader:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl",fragmentShader:"Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},CelVinyl:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_Cutoff:{value:.554},u_MainTex:{value:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl",fragmentShader:"CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},ChromaticWave:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_time:{value:new Vector4},u_EmissionGain:{value:.45}},vertexShader:"ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl",fragmentShader:"ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},CoarseBristles:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png"},u_Cutoff:{value:.25},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl",fragmentShader:"CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Comet:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png"},u_AlphaMask:{value:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png"},u_AlphaMask_TexelSize:{value:new Vector4(.0156,1,64,1)},u_time:{value:new Vector4},u_Speed:{value:1},u_EmissionGain:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl",fragmentShader:"Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},DiamondHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png"},u_time:{value:new Vector4},cameraPosition:{value:new Vector3},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl",fragmentShader:"DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Disco:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.65},u_SpecColor:{value:new Vector3(.5147059,.5147059,.5147059)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DotMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-vertex.glsl",fragmentShader:"DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Dots:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-MainTex.png"},u_TintColor:{value:new Vector4(1,1,1,1)},u_EmissionGain:{value:300},u_BaseGain:{value:.4}},vertexShader:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-vertex.glsl",fragmentShader:"Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},DoubleTaperedFlat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.15},u_SpecColor:{value:new Vector3(0,0,0)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-vertex.glsl",fragmentShader:"DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DoubleTaperedMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-vertex.glsl",fragmentShader:"DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},DuctTape:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.5372549,.5372549,.5372549)},u_Shininess:{value:.414},u_MainTex:{value:"DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-MainTex.png"},u_Cutoff:{value:.2},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-vertex.glsl",fragmentShader:"DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Electricity:{uniforms:{u_time:{value:new Vector4},u_DisplacementIntensity:{value:2},u_EmissionGain:{value:.2}},vertexShader:"Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-vertex.glsl",fragmentShader:"Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Embers:{uniforms:{u_time:{value:new Vector4},u_ScrollRate:{value:.6},u_ScrollDistance:{value:new Vector3(-.2,.6,0)},u_ScrollJitterIntensity:{value:.03},u_ScrollJitterFrequency:{value:5},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-MainTex.png"},u_Cutoff:{value:.2}},vertexShader:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-vertex.glsl",fragmentShader:"Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},EnvironmentDiffuse:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-vertex.glsl",fragmentShader:"EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},EnvironmentDiffuseLightMap:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-vertex.glsl",fragmentShader:"EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Fire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-MainTex.png"},u_time:{value:new Vector4},u_EmissionGain:{value:.5}},vertexShader:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-vertex.glsl",fragmentShader:"Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Flat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-vertex.glsl",fragmentShader:"Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Highlighter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-MainTex.png"},u_Cutoff:{value:.12}},vertexShader:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-vertex.glsl",fragmentShader:"Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Hypercolor:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.5},u_SpecColor:{value:new Vector3(.2745098,.2745098,.2745098)},u_MainTex:{value:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-MainTex.png"},u_time:{value:new Vector4},u_Cutoff:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-vertex.glsl",fragmentShader:"Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},HyperGrid:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-MainTex.png"}},vertexShader:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-vertex.glsl",fragmentShader:"HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Icing:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.15},u_Cutoff:{value:.5},u_MainTex:{value:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-vertex.glsl",fragmentShader:"Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Ink:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-vertex.glsl",fragmentShader:"Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Leaves:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.395},u_Cutoff:{value:.5},u_MainTex:{value:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-vertex.glsl",fragmentShader:"Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Light:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-MainTex.png"},u_EmissionGain:{value:.45}},vertexShader:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-vertex.glsl",fragmentShader:"Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},LightWire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Shininess:{value:.81},u_SpecColor:{value:new Vector3(.3455882,.3455882,.3455882)},u_time:{value:new Vector4},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_MainTex:{value:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-MainTex.png"}},vertexShader:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Lofted:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-vertex.glsl",fragmentShader:"Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Marker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-MainTex.png"},u_Cutoff:{value:.067},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-vertex.glsl",fragmentShader:"Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},MatteHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-vertex.glsl",fragmentShader:"MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},NeonPulse:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_time:{value:new Vector4},u_EmissionGain:{value:.5}},vertexShader:"NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-vertex.glsl",fragmentShader:"NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},OilPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.002,.002,512,512)}},vertexShader:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-vertex.glsl",fragmentShader:"OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Paper:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.145},u_Cutoff:{value:.16},u_MainTex:{value:"Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-vertex.glsl",fragmentShader:"Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},PbrTemplate:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-vertex.glsl",fragmentShader:"PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},PbrTransparentTemplate:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_Cutoff:{value:.2}},vertexShader:"PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-vertex.glsl",fragmentShader:"PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Petal:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.01},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-vertex.glsl",fragmentShader:"Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Plasma:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-MainTex.png"},u_time:{value:new Vector4}},vertexShader:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-vertex.glsl",fragmentShader:"Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Rainbow:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_EmissionGain:{value:.65}},vertexShader:"Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-vertex.glsl",fragmentShader:"Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},ShinyHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.1985294,.1985294,.1985294)},u_Shininess:{value:.743},u_Cutoff:{value:.5},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-vertex.glsl",fragmentShader:"ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Smoke:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-MainTex.png"}},vertexShader:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-vertex.glsl",fragmentShader:"Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Snow:{uniforms:{u_time:{value:new Vector4},u_ScrollRate:{value:.2},u_ScrollDistance:{value:new Vector3(0,-.3,0)},u_ScrollJitterIntensity:{value:.01},u_ScrollJitterFrequency:{value:12},u_TintColor:{value:new Vector4(1,1,1,1)},u_MainTex:{value:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-MainTex.png"}},vertexShader:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-vertex.glsl",fragmentShader:"Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},SoftHighlighter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-MainTex.png"}},vertexShader:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-vertex.glsl",fragmentShader:"SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:5,blendDstAlpha:201,blendDst:201,blendEquationAlpha:100,blendEquation:100,blendSrcAlpha:201,blendSrc:201},Spikes:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-vertex.glsl",fragmentShader:"Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Splatter:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-MainTex.png"},u_Cutoff:{value:.2},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-vertex.glsl",fragmentShader:"Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Stars:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_SparkleRate:{value:5.3},u_MainTex:{value:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-MainTex.png"}},vertexShader:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-vertex.glsl",fragmentShader:"Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Streamers:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-MainTex.png"},u_EmissionGain:{value:.4},u_time:{value:new Vector4}},vertexShader:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-vertex.glsl",fragmentShader:"Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Taffy:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-vertex.glsl",fragmentShader:"Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedFlat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_MainTex:{value:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-MainTex.png"},u_Cutoff:{value:.067},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-vertex.glsl",fragmentShader:"TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedMarker:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-vertex.glsl",fragmentShader:"TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},TaperedMarker_Flat:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(0,0,0)},u_Shininess:{value:.15},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_MainTex:{value:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-MainTex.png"},u_Cutoff:{value:.2}},vertexShader:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-vertex.glsl",fragmentShader:"TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},ThickPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.2352941,.2352941,.2352941)},u_Shininess:{value:.4},u_Cutoff:{value:.5},u_MainTex:{value:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-vertex.glsl",fragmentShader:"ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Toon:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},UnlitHull:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-vertex.glsl",fragmentShader:"UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},VelvetInk:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_MainTex:{value:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-vertex.glsl",fragmentShader:"VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},Waveform:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_EmissionGain:{value:.5178571},u_time:{value:new Vector4},u_MainTex:{value:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-MainTex.png"}},vertexShader:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-vertex.glsl",fragmentShader:"Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-fragment.glsl",side:2,transparent:true,depthFunc:2,depthWrite:false,depthTest:true,blending:2},WetPaint:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_SpecColor:{value:new Vector3(.1397059,.1397059,.1397059)},u_Shininess:{value:.85},u_Cutoff:{value:.3},u_MainTex:{value:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0},u_BumpMap:{value:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-BumpMap.png"},u_BumpMap_TexelSize:{value:new Vector4(.001,.0078,1024,128)}},vertexShader:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-vertex.glsl",fragmentShader:"WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},WigglyGraphite:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_time:{value:new Vector4},u_ambient_light_color:{value:new Vector4(.3922,.3922,.3922,1)},u_SceneLight_0_color:{value:new Vector4(.778,.8157,.9914,1)},u_SceneLight_1_color:{value:new Vector4(.4282,.4212,.3459,1)},u_Cutoff:{value:.5},u_MainTex:{value:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-MainTex.png"},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-vertex.glsl",fragmentShader:"WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0},Wire:{uniforms:{u_SceneLight_0_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_SceneLight_1_matrix:{value:[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},u_fogColor:{value:new Vector3(.0196,.0196,.0196)},u_fogDensity:{value:0}},vertexShader:"Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-vertex.glsl",fragmentShader:"Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-fragment.glsl",side:2,transparent:false,depthFunc:2,depthWrite:true,depthTest:true,blending:0}};class GLTFGoogleTiltBrushMaterialExtension{constructor(parser,brushPath){this.name="GOOGLE_tilt_brush_material";this.parser=parser;this.brushPath=brushPath;if(this.brushPath.slice(this.brushPath.length-1)!=="/"){this.brushPath+="/"}this.tiltShaderLoader=new TiltShaderLoader$1(parser.options.manager);this.tiltShaderLoader.setPath(brushPath);this.clock=new Clock}beforeRoot(){const parser=this.parser;const json=parser.json;if(!json.extensionsUsed||!json.extensionsUsed.includes(this.name)){return null}json.materials.forEach((material=>{const extensionsDef=material.extensions;if(!extensionsDef||!extensionsDef[this.name]){return}const guid=material.extensions.GOOGLE_tilt_brush_material.guid;const materialParams=this.tiltShaderLoader.lookupMaterial(guid);if(material?.pbrMetallicRoughness?.baseColorTexture){const mainTex=json.images[material.pbrMetallicRoughness.baseColorTexture.index];mainTex.uri=this.brushPath+materialParams.uniforms.u_MainTex.value}if(material?.normalTexture){const bumpMap=json.images[material.normalTexture.index];bumpMap.uri=this.brushPath+materialParams.uniforms.u_BumpMap.value}}))}afterRoot(glTF){const parser=this.parser;const json=parser.json;if(!json.extensionsUsed||!json.extensionsUsed.includes(this.name)){return null}const shaderResolves=[];for(const scene of glTF.scenes){scene.traverse((async object=>{const association=parser.associations.get(object);if(association===undefined||association.meshes===undefined){return}const mesh=json.meshes[association.meshes];mesh.primitives.forEach((prim=>{if(!prim.material){return}const mat=json.materials[prim.material];if(!mat.extensions||!mat.extensions[this.name]){return}const guid=mat.extensions.GOOGLE_tilt_brush_material.guid;shaderResolves.push(this.replaceMaterial(object,guid))}))}))}return Promise.all(shaderResolves)}async replaceMaterial(mesh,guid){let shader;switch(guid){case"0e87b49c-6546-3a34-3a44-8a556d7d6c3e":mesh.geometry.name="geometry_BlocksBasic";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksBasic");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksBasic";break;case"232998f8-d357-47a2-993a-53415df9be10":mesh.geometry.name="geometry_BlocksGem";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksGem");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGem";break;case"3d813d82-5839-4450-8ddc-8e889ecd96c7":mesh.geometry.name="geometry_BlocksGlass";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("BlocksGlass");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGlass";break;case"89d104cd-d012-426b-b5b3-bbaee63ac43c":mesh.geometry.name="geometry_Bubbles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Bubbles");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Bubbles";break;case"700f3aa8-9a7c-2384-8b8a-ea028905dd8c":mesh.geometry.name="geometry_CelVinyl";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("CelVinyl");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CelVinyl";break;case"0f0ff7b2-a677-45eb-a7d6-0cd7206f4816":mesh.geometry.name="geometry_ChromaticWave";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ChromaticWave");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ChromaticWave";break;case"1161af82-50cf-47db-9706-0c3576d43c43":case"79168f10-6961-464a-8be1-57ed364c5600":mesh.geometry.name="geometry_CoarseBristles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("CoarseBristles");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CoarseBristles";break;case"1caa6d7d-f015-3f54-3a4b-8b5354d39f81":mesh.geometry.name="geometry_Comet";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Comet");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Comet";break;case"c8313697-2563-47fc-832e-290f4c04b901":mesh.geometry.name="geometry_DiamondHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DiamondHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";break;case"4391aaaa-df73-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Disco";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Disco");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Disco";break;case"d1d991f2-e7a0-4cf1-b328-f57e915e6260":mesh.geometry.name="geometry_DotMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DotMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DotMarker";break;case"6a1cf9f9-032c-45ec-9b1d-a6680bee30f7":mesh.geometry.name="geometry_Dots";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Dots");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Dots";break;case"0d3889f3-3ede-470c-8af4-f44813306126":mesh.geometry.name="geometry_DoubleTaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DoubleTaperedFlat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedFlat";break;case"0d3889f3-3ede-470c-8af4-de4813306126":mesh.geometry.name="geometry_DoubleTaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DoubleTaperedMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedMarker";break;case"d0262945-853c-4481-9cbd-88586bed93cb":case"3ca16e2f-bdcd-4da2-8631-dcef342f40f1":mesh.geometry.name="geometry_DuctTape";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("DuctTape");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DuctTape";break;case"f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51":mesh.geometry.name="geometry_Electricity";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Electricity");mesh.material=shader;mesh.material.name="material_Electricity";break;case"02ffb866-7fb2-4d15-b761-1012cefb1360":mesh.geometry.name="geometry_Embers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Embers");mesh.material=shader;mesh.material.name="material_Embers";break;case"0ad58bbd-42bc-484e-ad9a-b61036ff4ce7":mesh.geometry.name="geometry_EnvironmentDiffuse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("EnvironmentDiffuse");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuse";break;case"d01d9d6c-9a61-4aba-8146-5891fafb013b":mesh.geometry.name="geometry_EnvironmentDiffuseLightMap";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("EnvironmentDiffuseLightMap");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuseLightMap";break;case"cb92b597-94ca-4255-b017-0e3f42f12f9e":mesh.geometry.name="geometry_Fire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Fire");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Fire";break;case"2d35bcf0-e4d8-452c-97b1-3311be063130":case"280c0a7a-aad8-416c-a7d2-df63d129ca70":case"55303bc4-c749-4a72-98d9-d23e68e76e18":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Flat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"cf019139-d41c-4eb0-a1d0-5cf54b0a42f3":mesh.geometry.name="geometry_Highlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Highlighter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Highlighter";break;case"dce872c2-7b49-4684-b59b-c45387949c5c":case"e8ef32b1-baa8-460a-9c2c-9cf8506794f5":mesh.geometry.name="geometry_Hypercolor";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Hypercolor");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Hypercolor";break;case"6a1cf9f9-032c-45ec-9b6e-a6680bee32e9":mesh.geometry.name="geometry_HyperGrid";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("HyperGrid");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_HyperGrid";break;case"2f212815-f4d3-c1a4-681a-feeaf9c6dc37":mesh.geometry.name="geometry_Icing";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Icing");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Icing";break;case"f5c336cf-5108-4b40-ade9-c687504385ab":case"c0012095-3ffd-4040-8ee1-fc180d346eaa":mesh.geometry.name="geometry_Ink";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Ink");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Ink";break;case"4a76a27a-44d8-4bfe-9a8c-713749a499b0":case"ea19de07-d0c0-4484-9198-18489a3c1487":mesh.geometry.name="geometry_Leaves";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Leaves");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Leaves";break;case"2241cd32-8ba2-48a5-9ee7-2caef7e9ed62":mesh.geometry.name="geometry_Light";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Light");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Light";break;case"4391aaaa-df81-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_LightWire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("LightWire");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_LightWire";break;case"d381e0f5-3def-4a0d-8853-31e9200bcbda":mesh.geometry.name="geometry_Lofted";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Lofted");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Lofted";break;case"429ed64a-4e97-4466-84d3-145a861ef684":mesh.geometry.name="geometry_Marker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Marker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Marker";break;case"79348357-432d-4746-8e29-0e25c112e3aa":mesh.geometry.name="geometry_MatteHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("MatteHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_MatteHull";break;case"b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6":mesh.geometry.name="geometry_NeonPulse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("NeonPulse");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_NeonPulse";break;case"f72ec0e7-a844-4e38-82e3-140c44772699":case"c515dad7-4393-4681-81ad-162ef052241b":mesh.geometry.name="geometry_OilPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("OilPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_OilPaint";break;case"f1114e2e-eb8d-4fde-915a-6e653b54e9f5":case"759f1ebd-20cd-4720-8d41-234e0da63716":mesh.geometry.name="geometry_Paper";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Paper");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Paper";break;case"f86a096c-2f4f-4f9d-ae19-81b99f2944e0":mesh.geometry.name="geometry_PbrTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("PbrTemplate");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTemplate";break;case"19826f62-42ac-4a9e-8b77-4231fbd0cfbf":mesh.geometry.name="geometry_PbrTransparentTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("PbrTransparentTemplate");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTransparentTemplate";break;case"e0abbc80-0f80-e854-4970-8924a0863dcc":mesh.geometry.name="geometry_Petal";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Petal");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Petal";break;case"c33714d1-b2f9-412e-bd50-1884c9d46336":mesh.geometry.name="geometry_Plasma";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Plasma");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Plasma";break;case"ad1ad437-76e2-450d-a23a-e17f8310b960":mesh.geometry.name="geometry_Rainbow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Rainbow");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Rainbow";break;case"faaa4d44-fcfb-4177-96be-753ac0421ba3":mesh.geometry.name="geometry_ShinyHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ShinyHull");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ShinyHull";break;case"70d79cca-b159-4f35-990c-f02193947fe8":mesh.geometry.name="geometry_Smoke";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Smoke");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Smoke";break;case"d902ed8b-d0d1-476c-a8de-878a79e3a34c":mesh.geometry.name="geometry_Snow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Snow");mesh.material=shader;mesh.material.name="material_Snow";break;case"accb32f5-4509-454f-93f8-1df3fd31df1b":mesh.geometry.name="geometry_SoftHighlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("SoftHighlighter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_SoftHighlighter";break;case"cf7f0059-7aeb-53a4-2b67-c83d863a9ffa":mesh.geometry.name="geometry_Spikes";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Spikes");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Spikes";break;case"8dc4a70c-d558-4efd-a5ed-d4e860f40dc3":case"7a1c8107-50c5-4b70-9a39-421576d6617e":mesh.geometry.name="geometry_Splatter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Splatter");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Splatter";break;case"0eb4db27-3f82-408d-b5a1-19ebd7d5b711":mesh.geometry.name="geometry_Stars";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("_tb_unity_normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("_tb_unity_texcoord_1"));shader=await this.tiltShaderLoader.loadAsync("Stars");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Stars";break;case"44bb800a-fbc3-4592-8426-94ecb05ddec3":mesh.geometry.name="geometry_Streamers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Streamers");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Streamers";break;case"0077f88c-d93a-42f3-b59b-b31c50cdb414":mesh.geometry.name="geometry_Taffy";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Taffy");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Taffy";break;case"b468c1fb-f254-41ed-8ec9-57030bc5660c":case"c8ccb53d-ae13-45ef-8afb-b730d81394eb":mesh.geometry.name="geometry_TaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("TaperedFlat");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedFlat";break;case"d90c6ad8-af0f-4b54-b422-e0f92abe1b3c":case"1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0":mesh.geometry.name="geometry_TaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("TaperedMarker");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedMarker";break;case"75b32cf0-fdd6-4d89-a64b-e2a00b247b0f":case"fdf0326a-c0d1-4fed-b101-9db0ff6d071f":mesh.geometry.name="geometry_ThickPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("ThickPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ThickPaint";break;case"4391385a-df73-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Toon";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("Toon");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Toon";break;case"a8fea537-da7c-4d4b-817f-24f074725d6d":mesh.geometry.name="geometry_UnlitHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("UnlitHull");mesh.material=shader;mesh.material.name="material_UnlitHull";break;case"d229d335-c334-495a-a801-660ac8a87360":mesh.geometry.name="geometry_VelvetInk";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("VelvetInk");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_VelvetInk";break;case"10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab":mesh.geometry.name="geometry_Waveform";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("Waveform");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Waveform";break;case"b67c0e81-ce6d-40a8-aeb0-ef036b081aa3":case"dea67637-cd1a-27e4-c9b1-52f4bbcb84e5":mesh.geometry.name="geometry_WetPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("WetPaint");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WetPaint";break;case"5347acf0-a8e2-47b6-8346-30c70719d763":case"e814fef1-97fd-7194-4a2f-50c2bb918be2":mesh.geometry.name="geometry_WigglyGraphite";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("_tb_unity_texcoord_0"));shader=await this.tiltShaderLoader.loadAsync("WigglyGraphite");shader.lights=true;shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WigglyGraphite";break;case"4391385a-cf83-4396-9e33-31e4e4930b27":mesh.geometry.name="geometry_Wire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=await this.tiltShaderLoader.loadAsync("Wire");mesh.material=shader;mesh.material.name="material_Wire";break;default:console.warn(`Could not find brush with guid ${guid}!`)}mesh.onBeforeRender=(renderer,scene,camera,geometry,material,group)=>{if(material.uniforms["u_time"]){const elapsedTime=this.clock.getElapsedTime();const time=new Vector4(elapsedTime/20,elapsedTime,elapsedTime*2,elapsedTime*3);material.uniforms["u_time"].value=time}if(material.uniforms["cameraPosition"]){material.uniforms["cameraPosition"].value=camera.position}if(material?.uniforms?.directionalLights?.value){if(material.uniforms.directionalLights.value[0]){if(material.uniforms.u_SceneLight_0_color){const color=material.uniforms.directionalLights.value[0].color;material.uniforms.u_SceneLight_0_color.value=new Vector4(color.r,color.g,color.b,1)}}if(material.uniforms.directionalLights.value[1]){if(material.uniforms.u_SceneLight_1_color){const color=material.uniforms.directionalLights.value[1].color;material.uniforms.u_SceneLight_1_color.value=new Vector4(color.r,color.g,color.b,1)}}}}}} /*! fflate - fast JavaScript compression/decompression Licensed under MIT. https://github.com/101arrowz/fflate/blob/master/LICENSE version 0.6.9 */var durl=function(c){return URL.createObjectURL(new Blob([c],{type:"text/javascript"}))};try{URL.revokeObjectURL(durl(""))}catch(e){durl=function(c){return"data:application/javascript;charset=UTF-8,"+encodeURI(c)}}var u8=Uint8Array,u16=Uint16Array,u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i=0;i<31;++i){b[i]=start+=1<>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1}var hMap=function(cd,mb,r){var s=cd.length;var i=0;var l=new u16(mb);for(;i>>rvb]=sv}}}}else{co=new u16(s);for(i=0;i>>15-cd[i]}}}return co};var flt=new u8(288);for(var i=0;i<144;++i)flt[i]=8;for(var i=144;i<256;++i)flt[i]=9;for(var i=256;i<280;++i)flt[i]=7;for(var i=280;i<288;++i)flt[i]=8;var fdt=new u8(32);for(var i=0;i<32;++i)fdt[i]=5;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i=1;im)m=a[i]}return m};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7)};var shft=function(p){return(p/8|0)+(p&7&&1)};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v instanceof u16?u16:v instanceof u32?u32:u8)(e-s);n.set(v.subarray(s,e));return n};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&!st.l&&sl<5)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l){var bl=buf.length;if(l>bl){var nbuf=new u8(Math.max(bl*2,l));nbuf.set(buf);buf=nbuf}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){st.f=final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)throw"unexpected EOF";break}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8;continue}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i=0;i>>4;if(s<16){ldt[i++]=s}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i++]=c}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1)}else throw"invalid block type";if(pos>tbts){if(noSt)throw"unexpected EOF";break}}if(noBuf)cbuf(bt+131072);var lms=(1<>>4;pos+=c&15;if(pos>tbts){if(noSt)throw"unexpected EOF";break}if(!c)throw"invalid length/literal";if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break}else{var add=sym-254;if(sym>264){var i=sym-257,b=fleb[i];add=bits(dat,pos,(1<>>4;if(!d)throw"invalid distance";pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<tbts){if(noSt)throw"unexpected EOF";break}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt>>0};var b8=function(d,b){return b4(d,b)+b4(d,b+4)*4294967296};function inflateSync(data,out){return inflt(data,out)}var td=typeof TextDecoder!="undefined"&&new TextDecoder;var tds=0;try{td.decode(et,{stream:true});tds=1}catch(e){}var dutf8=function(d){for(var r="",i=0;;){var c=d[i++];var eb=(c>127)+(c>223)+(c>239);if(i+eb>d.length)return[r,slc(d,i-1)];if(!eb)r+=String.fromCharCode(c);else if(eb==3){c=((c&15)<<18|(d[i++]&63)<<12|(d[i++]&63)<<6|d[i++]&63)-65536,r+=String.fromCharCode(55296|c>>10,56320|c&1023)}else if(eb&1)r+=String.fromCharCode((c&31)<<6|d[i++]&63);else r+=String.fromCharCode((c&15)<<12|(d[i++]&63)<<6|d[i++]&63)}};function strFromU8(dat,latin1){if(latin1){var r="";for(var i=0;i65558)throw"invalid zip file"}var c=b2(data,e+8);if(!c)return{};var o=b4(data,e+16);var z=o==4294967295;if(z){e=b4(data,e-12);if(b4(data,e)!=101075792)throw"invalid zip file";c=b4(data,e+32);o=b4(data,e+48)}for(var i=0;i=0){extensions[EXTENSIONS.KHR_MATERIALS_COMMON]=new GLTFMaterialsCommonExtension(json)}var parser=new GLTFParser(json,extensions,{crossOrigin:this.crossOrigin,manager:this.manager,path:path||this.resourcePath||""});parser.parse((function(scene,scenes,cameras,animations){var glTF={scene:scene,scenes:scenes,cameras:cameras,animations:animations};callback(glTF)}))}}function GLTFRegistry(){var objects={};return{get:function(key){return objects[key]},add:function(key,object){objects[key]=object},remove:function(key){delete objects[key]},removeAll:function(){objects={}},update:function(scene,camera){for(var name in objects){var object=objects[name];if(object.update){object.update(scene,camera)}}}}}class GLTFShader{constructor(targetNode,allNodes){var boundUniforms={};var uniforms=targetNode.material.uniforms;for(var uniformId in uniforms){var uniform=uniforms[uniformId];if(uniform.semantic){var sourceNodeRef=uniform.node;var sourceNode=targetNode;if(sourceNodeRef){sourceNode=allNodes[sourceNodeRef]}boundUniforms[uniformId]={semantic:uniform.semantic,sourceNode:sourceNode,targetNode:targetNode,uniform:uniform}}}this.boundUniforms=boundUniforms;this._m4=new Matrix4}update(scene,camera){var boundUniforms=this.boundUniforms;for(var name in boundUniforms){var boundUniform=boundUniforms[name];switch(boundUniform.semantic){case"MODELVIEW":var m4=boundUniform.uniform.value;m4.multiplyMatrices(camera.matrixWorldInverse,boundUniform.sourceNode.matrixWorld);break;case"MODELVIEWINVERSETRANSPOSE":var m3=boundUniform.uniform.value;this._m4.multiplyMatrices(camera.matrixWorldInverse,boundUniform.sourceNode.matrixWorld);m3.getNormalMatrix(this._m4);break;case"PROJECTION":var m4=boundUniform.uniform.value;m4.copy(camera.projectionMatrix);break;case"JOINTMATRIX":var m4v=boundUniform.uniform.value;for(var mi=0;mi__awaiter(this,void 0,void 0,(function*(){if(object.type==="Mesh"){const mesh=object;var shader;const targetFilter="brush_"+mesh.name.split("_")[1];switch(targetFilter){case"brush_BlocksBasic":mesh.geometry.name="geometry_BlocksBasic";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksBasic");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksBasic";break;case"brush_BlocksGem":mesh.geometry.name="geometry_BlocksGem";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksGem");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGem";break;case"brush_BlocksGlass":mesh.geometry.name="geometry_BlocksGlass";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksGlass");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGlass";break;case"brush_Bubbles":mesh.geometry.name="geometry_Bubbles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Bubbles");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Bubbles";break;case"brush_CelVinyl":mesh.geometry.name="geometry_CelVinyl";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("CelVinyl");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CelVinyl";break;case"brush_ChromaticWave":mesh.geometry.name="geometry_ChromaticWave";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ChromaticWave");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ChromaticWave";break;case"brush_CoarseBristles":mesh.geometry.name="geometry_CoarseBristles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("CoarseBristles");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CoarseBristles";break;case"brush_Comet":mesh.geometry.name="geometry_Comet";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Comet");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Comet";break;case"brush_DiamondHull":mesh.geometry.name="geometry_DiamondHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DiamondHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";break;case"brush_Disco":mesh.geometry.name="geometry_Disco";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Disco");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Disco";break;case"brush_DotMarker":mesh.geometry.name="geometry_DotMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DotMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DotMarker";break;case"brush_Dots":mesh.geometry.name="geometry_Dots";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Dots");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Dots";break;case"brush_DoubleTaperedFlat":mesh.geometry.name="geometry_DoubleTaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DoubleTaperedFlat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedFlat";break;case"brush_DoubleTaperedMarker":mesh.geometry.name="geometry_DoubleTaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DoubleTaperedMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedMarker";break;case"brush_DuctTape":mesh.geometry.name="geometry_DuctTape";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DuctTape");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DuctTape";break;case"brush_Electricity":mesh.geometry.name="geometry_Electricity";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Electricity");mesh.material=shader;mesh.material.name="material_Electricity";break;case"brush_Embers":mesh.geometry.name="geometry_Embers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Embers");mesh.material=shader;mesh.material.name="material_Embers";break;case"brush_EnvironmentDiffuse":mesh.geometry.name="geometry_EnvironmentDiffuse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("EnvironmentDiffuse");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuse";break;case"brush_EnvironmentDiffuseLightMap":mesh.geometry.name="geometry_EnvironmentDiffuseLightMap";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("EnvironmentDiffuseLightMap");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuseLightMap";break;case"brush_Fire":mesh.geometry.name="geometry_Fire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Fire");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Fire";break;case"brush_Flat":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Flat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"brush_FlatDeprecated":mesh.geometry.name="geometry_FlatDeprecated";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("FlatDeprecated");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_FlatDeprecated";break;case"brush_Highlighter":mesh.geometry.name="geometry_Highlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Highlighter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Highlighter";break;case"brush_Hypercolor":mesh.geometry.name="geometry_Hypercolor";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Hypercolor");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Hypercolor";break;case"brush_HyperGrid":mesh.geometry.name="geometry_HyperGrid";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("HyperGrid");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_HyperGrid";break;case"brush_Icing":mesh.geometry.name="geometry_Icing";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Icing");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Icing";break;case"brush_Ink":mesh.geometry.name="geometry_Ink";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Ink");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Ink";break;case"brush_Leaves":mesh.geometry.name="geometry_Leaves";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Leaves");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Leaves";break;case"brush_Light":mesh.geometry.name="geometry_Light";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Light");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Light";break;case"brush_LightWire":mesh.geometry.name="geometry_LightWire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("LightWire");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_LightWire";break;case"brush_Lofted":mesh.geometry.name="geometry_Lofted";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Lofted");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Lofted";break;case"brush_Marker":mesh.geometry.name="geometry_Marker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Marker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Marker";break;case"brush_MatteHull":mesh.geometry.name="geometry_MatteHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("MatteHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_MatteHull";break;case"brush_NeonPulse":mesh.geometry.name="geometry_NeonPulse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("NeonPulse");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_NeonPulse";break;case"brush_OilPaint":mesh.geometry.name="geometry_OilPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("OilPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_OilPaint";break;case"brush_Paper":mesh.geometry.name="geometry_Paper";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Paper");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Paper";break;case"brush_PbrTemplate":mesh.geometry.name="geometry_PbrTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("PbrTemplate");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTemplate";break;case"brush_PbrTransparentTemplate":mesh.geometry.name="geometry_PbrTransparentTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("PbrTransparentTemplate");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTransparentTemplate";break;case"brush_Petal":mesh.geometry.name="geometry_Petal";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Petal");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Petal";break;case"brush_Plasma":mesh.geometry.name="geometry_Plasma";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Plasma");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Plasma";break;case"brush_Rainbow":mesh.geometry.name="geometry_Rainbow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Rainbow");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Rainbow";break;case"brush_ShinyHull":mesh.geometry.name="geometry_ShinyHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ShinyHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ShinyHull";break;case"brush_Smoke":mesh.geometry.name="geometry_Smoke";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Smoke");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Smoke";break;case"brush_Snow":mesh.geometry.name="geometry_Snow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Snow");mesh.material=shader;mesh.material.name="material_Snow";break;case"brush_SoftHighlighter":mesh.geometry.name="geometry_SoftHighlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("SoftHighlighter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_SoftHighlighter";break;case"brush_Spikes":mesh.geometry.name="geometry_Spikes";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Spikes");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Spikes";break;case"brush_Splatter":mesh.geometry.name="geometry_Splatter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Splatter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Splatter";break;case"brush_Stars":mesh.geometry.name="geometry_Stars";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Stars");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Stars";break;case"brush_Streamers":mesh.geometry.name="geometry_Streamers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Streamers");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Streamers";break;case"brush_Taffy":mesh.geometry.name="geometry_Taffy";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DiamondHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";break;case"brush_TaperedFlat":mesh.geometry.name="geometry_TaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("TaperedFlat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedFlat";break;case"brush_TaperedMarker":mesh.geometry.name="geometry_TaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("TaperedMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedMarker";break;case"brush_TaperedMarker_Flat":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Flat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"brush_ThickPaint":mesh.geometry.name="geometry_ThickPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ThickPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ThickPaint";break;case"brush_Toon":mesh.geometry.name="geometry_Toon";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("Toon");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Toon";break;case"brush_UnlitHull":mesh.geometry.name="geometry_UnlitHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("UnlitHull");mesh.material=shader;mesh.material.name="material_UnlitHull";break;case"brush_VelvetInk":mesh.geometry.name="geometry_VelvetInk";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("VelvetInk");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_VelvetInk";break;case"brush_Waveform":mesh.geometry.name="geometry_Waveform";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Waveform");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Waveform";break;case"brush_WetPaint":mesh.geometry.name="geometry_WetPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("WetPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WetPaint";break;case"brush_WigglyGraphite":mesh.geometry.name="geometry_WigglyGraphite";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("WigglyGraphite");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WigglyGraphite";break;case"brush_Wire":mesh.geometry.name="geometry_Wire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("Wire");mesh.material=shader;mesh.material.name="material_Wire";break}mesh.onBeforeRender=(renderer,scene,camera,geometry,material,group)=>{if(material.uniforms["u_time"]){const elapsedTime=clock.getElapsedTime();const time=new Vector4(elapsedTime/20,elapsedTime,elapsedTime*2,elapsedTime*3);material.uniforms["u_time"].value=time}if(material.uniforms["cameraPosition"]){material.uniforms["cameraPosition"].value=camera.position}}}}))))}))}class Viewer{constructor(brushPath,frame){this.sceneColor=new Color("#000000");this.icosa_frame=frame;if(!this.icosa_frame)this.icosa_frame=document.getElementById("icosa-viewer");if(!this.icosa_frame){this.icosa_frame=document.createElement("div");this.icosa_frame.id="icosa-viewer"}const controlPanel=document.createElement("div");controlPanel.classList.add("control-panel");const fullscreenButton=document.createElement("button");fullscreenButton.classList.add("panel-button","fullscreen-button");fullscreenButton.onclick=()=>{this.toggleFullscreen(fullscreenButton)};controlPanel.appendChild(fullscreenButton);this.icosa_frame.appendChild(controlPanel);const loadscreen=document.createElement("div");loadscreen.id="loadscreen";const loadanim=document.createElement("div");loadanim.classList.add("loadlogo");loadscreen.appendChild(loadanim);this.icosa_frame.appendChild(loadscreen);loadscreen.addEventListener("transitionend",(function(){const opacity=window.getComputedStyle(loadscreen).opacity;if(parseFloat(opacity)<.2){loadscreen.classList.add("loaded")}}));const canvas=document.createElement("canvas");canvas.id="c";this.icosa_frame.appendChild(canvas);canvas.onmousedown=()=>{canvas.classList.add("grabbed")};canvas.onmouseup=()=>{canvas.classList.remove("grabbed")};const renderer=new WebGLRenderer({canvas:canvas});renderer.setPixelRatio(window.devicePixelRatio);renderer.outputEncoding=sRGBEncoding;renderer.xr.enabled=true;this.icosa_frame.appendChild(VRButton.createButton(renderer));const clock=new Clock;const fov=75;const aspect=2;const near=.1;const far=1e3;const flatCamera=new PerspectiveCamera(fov,aspect,near,far);flatCamera.position.set(10,10,10);CameraControls.install({THREE:subsetOfTHREE});this.cameraControls=new CameraControls(flatCamera,canvas);this.cameraControls.dampingFactor=.1;this.cameraControls.polarRotateSpeed=this.cameraControls.azimuthRotateSpeed=.5;this.cameraControls.setTarget(0,0,0);this.cameraControls.dollyTo(3,true);flatCamera.updateProjectionMatrix();this.sceneCamera=flatCamera;const xrCamera=new PerspectiveCamera(fov,aspect,near,far);xrCamera.updateProjectionMatrix();setupNavigation(this.cameraControls);this.scene=new Scene;const viewer=this;const manager=new LoadingManager;manager.onStart=function(){var _a,_b;(_a=document.getElementById("loadscreen"))===null||_a===void 0?void 0:_a.classList.remove("fade-out");(_b=document.getElementById("loadscreen"))===null||_b===void 0?void 0:_b.classList.remove("loaded")};manager.onLoad=function(){var _a;(_a=document.getElementById("loadscreen"))===null||_a===void 0?void 0:_a.classList.add("fade-out")};this.brushPath=brushPath;this.tiltLoader=new TiltLoader(manager);this.tiltLoader.setBrushPath(this.brushPath);this.gltfLegacyLoader=new LegacyGLTFLoader(manager);this.gltfLoader=new GLTFLoader(manager);this.gltfLoader.register((parser=>new GLTFGoogleTiltBrushMaterialExtension(parser,this.brushPath)));const dracoLoader=new DRACOLoader;dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");this.gltfLoader.setDRACOLoader(dracoLoader);function animate(){renderer.setAnimationLoop(render)}function render(){const delta=clock.getDelta();if(renderer.xr.isPresenting){viewer.sceneCamera=xrCamera}else{viewer.sceneCamera=flatCamera;const needResize=canvas.width!==canvas.clientWidth||canvas.height!==canvas.clientHeight;if(needResize){renderer.setSize(canvas.clientWidth,canvas.clientHeight,false);flatCamera.aspect=canvas.clientWidth/canvas.clientHeight;flatCamera.updateProjectionMatrix()}viewer.cameraControls.update(delta)}renderer.render(viewer.scene,viewer.sceneCamera)}animate()}toggleFullscreen(controlButton){var _a,_b;if((_a=this.icosa_frame)===null||_a===void 0?void 0:_a.requestFullscreen)(_b=this.icosa_frame)===null||_b===void 0?void 0:_b.requestFullscreen();document.onfullscreenchange=()=>{if(document.fullscreenElement==null){controlButton.onclick=()=>{var _a,_b;if((_a=this.icosa_frame)===null||_a===void 0?void 0:_a.requestFullscreen)(_b=this.icosa_frame)===null||_b===void 0?void 0:_b.requestFullscreen()};controlButton.classList.remove("fullscreen")}else{controlButton.onclick=()=>{if(document.exitFullscreen)document.exitFullscreen()};controlButton.classList.add("fullscreen")}}}initializeScene(){if(!this.loadedModel)return;this.scene.clear();this.scene.background=this.sceneColor;this.scene.add(this.loadedModel);const box=(new Box3).setFromObject(this.loadedModel);const boxSize=box.getSize(new Vector3).length();const boxCenter=box.getCenter(new Vector3);this.cameraControls.minDistance=boxSize*.01;this.cameraControls.maxDistance=boxSize;const midDistance=this.cameraControls.minDistance+(this.cameraControls.maxDistance-this.cameraControls.minDistance)/2;this.cameraControls.setTarget(boxCenter.x,boxCenter.y,boxCenter.z);this.cameraControls.dollyTo(midDistance,true);this.cameraControls.saveState();const ambientLight=new AmbientLight;this.scene.add(ambientLight)}loadGltf(url){return __awaiter(this,void 0,void 0,(function*(){const gltf=yield this.gltfLoader.loadAsync(url);this.loadedModel=gltf.scene;this.initializeScene()}))}loadTilt(url){return __awaiter(this,void 0,void 0,(function*(){const tiltData=yield this.tiltLoader.loadAsync(url);this.loadedModel=tiltData;this.initializeScene()}))}loadObj(url){}loadGltf1(url){return __awaiter(this,void 0,void 0,(function*(){const tiltData=yield this.gltfLegacyLoader.loadAsync(url);this.loadedModel=tiltData.scene;yield replaceBrushMaterials(this.brushPath,this.loadedModel);this.initializeScene()}))}}exports.Viewer=Viewer;Object.defineProperty(exports,"__esModule",{value:true})})); \ No newline at end of file + ***************************************************************************** */var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(b.hasOwnProperty(p))d[p]=b[p]};return extendStatics(d,b)};function __extends(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}var EventDispatcher=function(){function EventDispatcher(){this._listeners={}}EventDispatcher.prototype.addEventListener=function(type,listener){var listeners=this._listeners;if(listeners[type]===undefined)listeners[type]=[];if(listeners[type].indexOf(listener)===-1)listeners[type].push(listener)};EventDispatcher.prototype.removeEventListener=function(type,listener){var listeners=this._listeners;var listenerArray=listeners[type];if(listenerArray!==undefined){var index=listenerArray.indexOf(listener);if(index!==-1)listenerArray.splice(index,1)}};EventDispatcher.prototype.dispatchEvent=function(event){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=0){extensions[EXTENSIONS.KHR_MATERIALS_COMMON]=new GLTFMaterialsCommonExtension(json)}var parser=new GLTFParser(json,extensions,{crossOrigin:this.crossOrigin,manager:this.manager,path:path||this.resourcePath||""});parser.parse((function(scene,scenes,cameras,animations){var glTF={scene:scene,scenes:scenes,cameras:cameras,animations:animations};callback(glTF)}))}}function GLTFRegistry(){var objects={};return{get:function(key){return objects[key]},add:function(key,object){objects[key]=object},remove:function(key){delete objects[key]},removeAll:function(){objects={}},update:function(scene,camera){for(var name in objects){var object=objects[name];if(object.update){object.update(scene,camera)}}}}}class GLTFShader{constructor(targetNode,allNodes){var boundUniforms={};var uniforms=targetNode.material.uniforms;for(var uniformId in uniforms){var uniform=uniforms[uniformId];if(uniform.semantic){var sourceNodeRef=uniform.node;var sourceNode=targetNode;if(sourceNodeRef){sourceNode=allNodes[sourceNodeRef]}boundUniforms[uniformId]={semantic:uniform.semantic,sourceNode:sourceNode,targetNode:targetNode,uniform:uniform}}}this.boundUniforms=boundUniforms;this._m4=new Matrix4}update(scene,camera){var boundUniforms=this.boundUniforms;for(var name in boundUniforms){var boundUniform=boundUniforms[name];switch(boundUniform.semantic){case"MODELVIEW":var m4=boundUniform.uniform.value;m4.multiplyMatrices(camera.matrixWorldInverse,boundUniform.sourceNode.matrixWorld);break;case"MODELVIEWINVERSETRANSPOSE":var m3=boundUniform.uniform.value;this._m4.multiplyMatrices(camera.matrixWorldInverse,boundUniform.sourceNode.matrixWorld);m3.getNormalMatrix(this._m4);break;case"PROJECTION":var m4=boundUniform.uniform.value;m4.copy(camera.projectionMatrix);break;case"JOINTMATRIX":var m4v=boundUniform.uniform.value;for(var mi=0;mi__awaiter(this,void 0,void 0,(function*(){if(object.type==="Mesh"){const mesh=object;var shader;const targetFilter="brush_"+mesh.name.split("_")[1];switch(targetFilter){case"brush_BlocksBasic":mesh.geometry.name="geometry_BlocksBasic";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksBasic");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksBasic";break;case"brush_BlocksGem":mesh.geometry.name="geometry_BlocksGem";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksGem");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGem";break;case"brush_BlocksGlass":mesh.geometry.name="geometry_BlocksGlass";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("BlocksGlass");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_BlocksGlass";break;case"brush_Bubbles":mesh.geometry.name="geometry_Bubbles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Bubbles");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Bubbles";break;case"brush_CelVinyl":mesh.geometry.name="geometry_CelVinyl";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("CelVinyl");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CelVinyl";break;case"brush_ChromaticWave":mesh.geometry.name="geometry_ChromaticWave";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ChromaticWave");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ChromaticWave";break;case"brush_CoarseBristles":mesh.geometry.name="geometry_CoarseBristles";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("CoarseBristles");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_CoarseBristles";break;case"brush_Comet":mesh.geometry.name="geometry_Comet";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Comet");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Comet";break;case"brush_DiamondHull":mesh.geometry.name="geometry_DiamondHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DiamondHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";break;case"brush_Disco":mesh.geometry.name="geometry_Disco";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Disco");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Disco";break;case"brush_DotMarker":mesh.geometry.name="geometry_DotMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DotMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DotMarker";break;case"brush_Dots":mesh.geometry.name="geometry_Dots";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Dots");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Dots";break;case"brush_DoubleTaperedFlat":mesh.geometry.name="geometry_DoubleTaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DoubleTaperedFlat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedFlat";break;case"brush_DoubleTaperedMarker":mesh.geometry.name="geometry_DoubleTaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DoubleTaperedMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DoubleTaperedMarker";break;case"brush_DuctTape":mesh.geometry.name="geometry_DuctTape";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DuctTape");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DuctTape";break;case"brush_Electricity":mesh.geometry.name="geometry_Electricity";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Electricity");mesh.material=shader;mesh.material.name="material_Electricity";break;case"brush_Embers":mesh.geometry.name="geometry_Embers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Embers");mesh.material=shader;mesh.material.name="material_Embers";break;case"brush_EnvironmentDiffuse":mesh.geometry.name="geometry_EnvironmentDiffuse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("EnvironmentDiffuse");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuse";break;case"brush_EnvironmentDiffuseLightMap":mesh.geometry.name="geometry_EnvironmentDiffuseLightMap";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("EnvironmentDiffuseLightMap");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_EnvironmentDiffuseLightMap";break;case"brush_Fire":mesh.geometry.name="geometry_Fire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Fire");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Fire";break;case"brush_Flat":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Flat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"brush_FlatDeprecated":mesh.geometry.name="geometry_FlatDeprecated";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("FlatDeprecated");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_FlatDeprecated";break;case"brush_Highlighter":mesh.geometry.name="geometry_Highlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Highlighter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Highlighter";break;case"brush_Hypercolor":mesh.geometry.name="geometry_Hypercolor";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Hypercolor");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Hypercolor";break;case"brush_HyperGrid":mesh.geometry.name="geometry_HyperGrid";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("HyperGrid");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_HyperGrid";break;case"brush_Icing":mesh.geometry.name="geometry_Icing";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Icing");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Icing";break;case"brush_Ink":mesh.geometry.name="geometry_Ink";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Ink");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Ink";break;case"brush_Leaves":mesh.geometry.name="geometry_Leaves";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Leaves");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Leaves";break;case"brush_Light":mesh.geometry.name="geometry_Light";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Light");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Light";break;case"brush_LightWire":mesh.geometry.name="geometry_LightWire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("LightWire");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_LightWire";break;case"brush_Lofted":mesh.geometry.name="geometry_Lofted";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Lofted");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Lofted";break;case"brush_Marker":mesh.geometry.name="geometry_Marker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Marker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Marker";break;case"brush_MatteHull":mesh.geometry.name="geometry_MatteHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("MatteHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_MatteHull";break;case"brush_NeonPulse":mesh.geometry.name="geometry_NeonPulse";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("NeonPulse");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_NeonPulse";break;case"brush_OilPaint":mesh.geometry.name="geometry_OilPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("OilPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_OilPaint";break;case"brush_Paper":mesh.geometry.name="geometry_Paper";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Paper");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Paper";break;case"brush_PbrTemplate":mesh.geometry.name="geometry_PbrTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("PbrTemplate");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTemplate";break;case"brush_PbrTransparentTemplate":mesh.geometry.name="geometry_PbrTransparentTemplate";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("PbrTransparentTemplate");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_PbrTransparentTemplate";break;case"brush_Petal":mesh.geometry.name="geometry_Petal";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Petal");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Petal";break;case"brush_Plasma":mesh.geometry.name="geometry_Plasma";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Plasma");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Plasma";break;case"brush_Rainbow":mesh.geometry.name="geometry_Rainbow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Rainbow");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Rainbow";break;case"brush_ShinyHull":mesh.geometry.name="geometry_ShinyHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ShinyHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ShinyHull";break;case"brush_Smoke":mesh.geometry.name="geometry_Smoke";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Smoke");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Smoke";break;case"brush_Snow":mesh.geometry.name="geometry_Snow";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Snow");mesh.material=shader;mesh.material.name="material_Snow";break;case"brush_SoftHighlighter":mesh.geometry.name="geometry_SoftHighlighter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("SoftHighlighter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_SoftHighlighter";break;case"brush_Spikes":mesh.geometry.name="geometry_Spikes";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Spikes");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Spikes";break;case"brush_Splatter":mesh.geometry.name="geometry_Splatter";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Splatter");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Splatter";break;case"brush_Stars":mesh.geometry.name="geometry_Stars";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));mesh.geometry.setAttribute("a_texcoord1",mesh.geometry.getAttribute("uv2"));shader=yield tiltShaderLoader.loadAsync("Stars");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Stars";break;case"brush_Streamers":mesh.geometry.name="geometry_Streamers";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Streamers");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Streamers";break;case"brush_Taffy":mesh.geometry.name="geometry_Taffy";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("DiamondHull");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_DiamondHull";break;case"brush_TaperedFlat":mesh.geometry.name="geometry_TaperedFlat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("TaperedFlat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedFlat";break;case"brush_TaperedMarker":mesh.geometry.name="geometry_TaperedMarker";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("TaperedMarker");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_TaperedMarker";break;case"brush_TaperedMarker_Flat":mesh.geometry.name="geometry_Flat";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Flat");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Flat";break;case"brush_ThickPaint":mesh.geometry.name="geometry_ThickPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("ThickPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_ThickPaint";break;case"brush_Toon":mesh.geometry.name="geometry_Toon";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("Toon");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Toon";break;case"brush_UnlitHull":mesh.geometry.name="geometry_UnlitHull";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("UnlitHull");mesh.material=shader;mesh.material.name="material_UnlitHull";break;case"brush_VelvetInk":mesh.geometry.name="geometry_VelvetInk";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("VelvetInk");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_VelvetInk";break;case"brush_Waveform":mesh.geometry.name="geometry_Waveform";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("Waveform");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_Waveform";break;case"brush_WetPaint":mesh.geometry.name="geometry_WetPaint";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("WetPaint");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WetPaint";break;case"brush_WigglyGraphite":mesh.geometry.name="geometry_WigglyGraphite";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));mesh.geometry.setAttribute("a_texcoord0",mesh.geometry.getAttribute("uv"));shader=yield tiltShaderLoader.loadAsync("WigglyGraphite");shader.uniformsNeedUpdate=true;mesh.material=shader;mesh.material.name="material_WigglyGraphite";break;case"brush_Wire":mesh.geometry.name="geometry_Wire";mesh.geometry.setAttribute("a_position",mesh.geometry.getAttribute("position"));mesh.geometry.setAttribute("a_normal",mesh.geometry.getAttribute("normal"));mesh.geometry.setAttribute("a_color",mesh.geometry.getAttribute("color"));shader=yield tiltShaderLoader.loadAsync("Wire");mesh.material=shader;mesh.material.name="material_Wire";break}mesh.onBeforeRender=(renderer,scene,camera,geometry,material,group)=>{if(material.uniforms["u_time"]){const elapsedTime=clock.getElapsedTime();const time=new Vector4(elapsedTime/20,elapsedTime,elapsedTime*2,elapsedTime*3);material.uniforms["u_time"].value=time}if(material.uniforms["cameraPosition"]){material.uniforms["cameraPosition"].value=camera.position}}}}))))}))}class Viewer{constructor(brushPath,frame){this.sceneColor=new Color("#000000");this.icosa_frame=frame;if(!this.icosa_frame)this.icosa_frame=document.getElementById("icosa-viewer");if(!this.icosa_frame){this.icosa_frame=document.createElement("div");this.icosa_frame.id="icosa-viewer"}const controlPanel=document.createElement("div");controlPanel.classList.add("control-panel");const fullscreenButton=document.createElement("button");fullscreenButton.classList.add("panel-button","fullscreen-button");fullscreenButton.onclick=()=>{this.toggleFullscreen(fullscreenButton)};controlPanel.appendChild(fullscreenButton);this.icosa_frame.appendChild(controlPanel);const loadscreen=document.createElement("div");loadscreen.id="loadscreen";const loadanim=document.createElement("div");loadanim.classList.add("loadlogo");loadscreen.appendChild(loadanim);this.icosa_frame.appendChild(loadscreen);loadscreen.addEventListener("transitionend",(function(){const opacity=window.getComputedStyle(loadscreen).opacity;if(parseFloat(opacity)<.2){loadscreen.classList.add("loaded")}}));const canvas=document.createElement("canvas");canvas.id="c";this.icosa_frame.appendChild(canvas);canvas.onmousedown=()=>{canvas.classList.add("grabbed")};canvas.onmouseup=()=>{canvas.classList.remove("grabbed")};const renderer=new WebGLRenderer({canvas:canvas});renderer.setPixelRatio(window.devicePixelRatio);renderer.outputEncoding=sRGBEncoding;renderer.xr.enabled=true;this.icosa_frame.appendChild(VRButton.createButton(renderer));const clock=new Clock;const fov=75;const aspect=2;const near=.1;const far=1e3;const flatCamera=new PerspectiveCamera(fov,aspect,near,far);flatCamera.position.set(10,10,10);CameraControls.install({THREE:subsetOfTHREE});this.cameraControls=new CameraControls(flatCamera,canvas);this.cameraControls.dampingFactor=.1;this.cameraControls.polarRotateSpeed=this.cameraControls.azimuthRotateSpeed=.5;this.cameraControls.setTarget(0,0,0);this.cameraControls.dollyTo(3,true);flatCamera.updateProjectionMatrix();this.sceneCamera=flatCamera;const xrCamera=new PerspectiveCamera(fov,aspect,near,far);xrCamera.updateProjectionMatrix();setupNavigation(this.cameraControls);this.scene=new Scene;const viewer=this;const manager=new LoadingManager;manager.onStart=function(){var _a,_b;(_a=document.getElementById("loadscreen"))===null||_a===void 0?void 0:_a.classList.remove("fade-out");(_b=document.getElementById("loadscreen"))===null||_b===void 0?void 0:_b.classList.remove("loaded")};manager.onLoad=function(){var _a;(_a=document.getElementById("loadscreen"))===null||_a===void 0?void 0:_a.classList.add("fade-out")};this.brushPath=brushPath;this.tiltLoader=new TiltLoader(manager);this.tiltLoader.setBrushPath(this.brushPath);this.gltfLegacyLoader=new LegacyGLTFLoader(manager);this.gltfLoader=new GLTFLoader(manager);this.gltfLoader.register((parser=>new GLTFGoogleTiltBrushMaterialExtension(parser,this.brushPath)));const dracoLoader=new DRACOLoader;dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");this.gltfLoader.setDRACOLoader(dracoLoader);function animate(){renderer.setAnimationLoop(render)}function render(){const delta=clock.getDelta();if(renderer.xr.isPresenting){viewer.sceneCamera=xrCamera}else{viewer.sceneCamera=flatCamera;const needResize=canvas.width!==canvas.clientWidth||canvas.height!==canvas.clientHeight;if(needResize){renderer.setSize(canvas.clientWidth,canvas.clientHeight,false);flatCamera.aspect=canvas.clientWidth/canvas.clientHeight;flatCamera.updateProjectionMatrix()}viewer.cameraControls.update(delta)}renderer.render(viewer.scene,viewer.sceneCamera)}animate()}toggleFullscreen(controlButton){var _a,_b;if((_a=this.icosa_frame)===null||_a===void 0?void 0:_a.requestFullscreen)(_b=this.icosa_frame)===null||_b===void 0?void 0:_b.requestFullscreen();document.onfullscreenchange=()=>{if(document.fullscreenElement==null){controlButton.onclick=()=>{var _a,_b;if((_a=this.icosa_frame)===null||_a===void 0?void 0:_a.requestFullscreen)(_b=this.icosa_frame)===null||_b===void 0?void 0:_b.requestFullscreen()};controlButton.classList.remove("fullscreen")}else{controlButton.onclick=()=>{if(document.exitFullscreen)document.exitFullscreen()};controlButton.classList.add("fullscreen")}}}initializeScene(){if(!this.loadedModel)return;this.scene.clear();this.scene.background=this.sceneColor;this.scene.add(this.loadedModel);const box=(new Box3).setFromObject(this.loadedModel);const boxSize=box.getSize(new Vector3).length();const boxCenter=box.getCenter(new Vector3);this.cameraControls.minDistance=boxSize*.01;this.cameraControls.maxDistance=boxSize;const midDistance=this.cameraControls.minDistance+(this.cameraControls.maxDistance-this.cameraControls.minDistance)/2;this.cameraControls.setTarget(boxCenter.x,boxCenter.y,boxCenter.z);this.cameraControls.dollyTo(midDistance,true);this.cameraControls.saveState();const ambientLight=new AmbientLight;this.scene.add(ambientLight)}loadGltf(url){return __awaiter(this,void 0,void 0,(function*(){const gltf=yield this.gltfLoader.loadAsync(url);this.loadedModel=gltf.scene;this.initializeScene()}))}loadTilt(url){return __awaiter(this,void 0,void 0,(function*(){const tiltData=yield this.tiltLoader.loadAsync(url);this.loadedModel=tiltData;this.initializeScene()}))}loadObj(url){}loadGltf1(url){return __awaiter(this,void 0,void 0,(function*(){const tiltData=yield this.gltfLegacyLoader.loadAsync(url);this.loadedModel=tiltData.scene;yield replaceBrushMaterials(this.brushPath,this.loadedModel);this.initializeScene()}))}}exports.Viewer=Viewer;Object.defineProperty(exports,"__esModule",{value:true})})); \ No newline at end of file diff --git a/dist/icosa-viewer.module.js b/dist/icosa-viewer.module.js index e334c29..c72b43e 100644 --- a/dist/icosa-viewer.module.js +++ b/dist/icosa-viewer.module.js @@ -1,7 +1,7 @@ /*! * Icosa Viewer * https://github.com/icosa-gallery/icosa-viewer - * Copyright (c) 2021 Icosa Gallery + * Copyright (c) 2021-2022 Icosa Gallery * Released under the Apache 2.0 Licence. */ /*! ***************************************************************************** @@ -1454,10 +1454,10 @@ function createBoundingSphere(object3d, out) { /** * @license - * Copyright 2010-2021 Three.js Authors + * Copyright 2010-2022 Three.js Authors * SPDX-License-Identifier: MIT */ -const REVISION = '128'; +const REVISION = '139'; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const CullFaceNone = 0; const CullFaceBack = 1; @@ -1515,7 +1515,6 @@ const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; -const CubeUVRefractionMapping = 307; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; @@ -1535,7 +1534,6 @@ const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; -const UnsignedShort565Type = 1019; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBFormat = 1022; @@ -1548,7 +1546,6 @@ const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; -const RGBIntegerFormat = 1032; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; @@ -1577,20 +1574,6 @@ const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; -const SRGB8_ALPHA8_ASTC_4x4_Format = 37840; -const SRGB8_ALPHA8_ASTC_5x4_Format = 37841; -const SRGB8_ALPHA8_ASTC_5x5_Format = 37842; -const SRGB8_ALPHA8_ASTC_6x5_Format = 37843; -const SRGB8_ALPHA8_ASTC_6x6_Format = 37844; -const SRGB8_ALPHA8_ASTC_8x5_Format = 37845; -const SRGB8_ALPHA8_ASTC_8x6_Format = 37846; -const SRGB8_ALPHA8_ASTC_8x8_Format = 37847; -const SRGB8_ALPHA8_ASTC_10x5_Format = 37848; -const SRGB8_ALPHA8_ASTC_10x6_Format = 37849; -const SRGB8_ALPHA8_ASTC_10x8_Format = 37850; -const SRGB8_ALPHA8_ASTC_10x10_Format = 37851; -const SRGB8_ALPHA8_ASTC_12x10_Format = 37852; -const SRGB8_ALPHA8_ASTC_12x12_Format = 37853; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; @@ -1607,16 +1590,12 @@ const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; const LinearEncoding = 3000; const sRGBEncoding = 3001; -const GammaEncoding = 3007; -const RGBEEncoding = 3002; -const LogLuvEncoding = 3003; -const RGBM7Encoding = 3004; -const RGBM16Encoding = 3005; -const RGBDEncoding = 3006; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; +const SRGBColorSpace = 'srgb'; +const LinearSRGBColorSpace = 'srgb-linear'; const KeepStencilOp = 7680; const AlwaysStencilFunc = 519; @@ -1624,6 +1603,8 @@ const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const GLSL3 = '300 es'; +const _SRGBAFormat = 1035; // fallback for WebGL 1 + /** * https://github.com/mrdoob/eventdispatcher.js/ */ @@ -1735,8 +1716,8 @@ function generateUUID() { _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 ]; - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); + // .toLowerCase() here flattens concatenated strings to save heap memory space. + return uuid.toLowerCase(); } @@ -1746,7 +1727,7 @@ function clamp( value, min, max ) { } -// compute euclidian modulo of m % n +// compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo( n, m ) { @@ -1768,11 +1749,11 @@ function inverseLerp( x, y, value ) { return ( value - x ) / ( y - x ); - } else { + } else { return 0; - } + } } @@ -1844,13 +1825,17 @@ function randFloatSpread( range ) { // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom( s ) { - if ( s !== undefined ) _seed = s % 2147483647; + if ( s !== undefined ) _seed = s; - // Park-Miller algorithm + // Mulberry32 generator - _seed = _seed * 16807 % 2147483647; + let t = _seed += 0x6D2B79F5; - return ( _seed - 1 ) / 2147483646; + t = Math.imul( t ^ t >>> 15, t | 1 ); + + t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); + + return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; } @@ -1940,6 +1925,70 @@ function setQuaternionFromProperEuler( q, a, b, c, order ) { } +function denormalize$1( value, array ) { + + switch ( array.constructor ) { + + case Float32Array: + + return value; + + case Uint16Array: + + return value / 65535.0; + + case Uint8Array: + + return value / 255.0; + + case Int16Array: + + return Math.max( value / 32767.0, - 1.0 ); + + case Int8Array: + + return Math.max( value / 127.0, - 1.0 ); + + default: + + throw new Error( 'Invalid component type.' ); + + } + +} + +function normalize( value, array ) { + + switch ( array.constructor ) { + + case Float32Array: + + return value; + + case Uint16Array: + + return Math.round( value * 65535.0 ); + + case Uint8Array: + + return Math.round( value * 255.0 ); + + case Int16Array: + + return Math.round( value * 32767.0 ); + + case Int8Array: + + return Math.round( value * 127.0 ); + + default: + + throw new Error( 'Invalid component type.' ); + + } + +} + var MathUtils = /*#__PURE__*/Object.freeze({ __proto__: null, DEG2RAD: DEG2RAD, @@ -1963,7 +2012,9 @@ var MathUtils = /*#__PURE__*/Object.freeze({ isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, - setQuaternionFromProperEuler: setQuaternionFromProperEuler + setQuaternionFromProperEuler: setQuaternionFromProperEuler, + normalize: normalize, + denormalize: denormalize$1 }); class Vector2 { @@ -2438,6 +2489,13 @@ class Vector2 { } + *[ Symbol.iterator ]() { + + yield this.x; + yield this.y; + + } + } Vector2.prototype.isVector2 = true; @@ -2780,322 +2838,323 @@ class Matrix3 { Matrix3.prototype.isMatrix3 = true; -let _canvas; +function arrayNeedsUint32( array ) { -class ImageUtils { + // assumes larger values usually on last - static getDataURL( image ) { + for ( let i = array.length - 1; i >= 0; -- i ) { - if ( /^data:/i.test( image.src ) ) { + if ( array[ i ] > 65535 ) return true; - return image.src; + } - } + return false; - if ( typeof HTMLCanvasElement == 'undefined' ) { +} - return image.src; +function createElementNS( name ) { - } + return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); - let canvas; +} - if ( image instanceof HTMLCanvasElement ) { +function SRGBToLinear( c ) { - canvas = image; + return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - } else { +} - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); +function LinearToSRGB( c ) { - _canvas.width = image.width; - _canvas.height = image.height; + return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - const context = _canvas.getContext( '2d' ); +} - if ( image instanceof ImageData ) { +// JavaScript RGB-to-RGB transforms, defined as +// FN[InputColorSpace][OutputColorSpace] callback functions. +const FN = { + [ SRGBColorSpace ]: { [ LinearSRGBColorSpace ]: SRGBToLinear }, + [ LinearSRGBColorSpace ]: { [ SRGBColorSpace ]: LinearToSRGB }, +}; - context.putImageData( image, 0, 0 ); +const ColorManagement = { - } else { + legacyMode: true, - context.drawImage( image, 0, 0, image.width, image.height ); + get workingColorSpace() { - } + return LinearSRGBColorSpace; - canvas = _canvas; + }, - } + set workingColorSpace( colorSpace ) { - if ( canvas.width > 2048 || canvas.height > 2048 ) { + console.warn( 'THREE.ColorManagement: .workingColorSpace is readonly.' ); - console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); + }, - return canvas.toDataURL( 'image/jpeg', 0.6 ); + convert: function ( color, sourceColorSpace, targetColorSpace ) { - } else { + if ( this.legacyMode || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { - return canvas.toDataURL( 'image/png' ); + return color; } - } + if ( FN[ sourceColorSpace ] && FN[ sourceColorSpace ][ targetColorSpace ] !== undefined ) { -} + const fn = FN[ sourceColorSpace ][ targetColorSpace ]; -let textureId = 0; + color.r = fn( color.r ); + color.g = fn( color.g ); + color.b = fn( color.b ); -class Texture extends EventDispatcher$1 { + return color; - constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { + } - super(); + throw new Error( 'Unsupported color space conversion.' ); - Object.defineProperty( this, 'id', { value: textureId ++ } ); + }, - this.uuid = generateUUID(); + fromWorkingColorSpace: function ( color, targetColorSpace ) { - this.name = ''; + return this.convert( color, this.workingColorSpace, targetColorSpace ); - this.image = image; - this.mipmaps = []; + }, - this.mapping = mapping; + toWorkingColorSpace: function ( color, sourceColorSpace ) { - this.wrapS = wrapS; - this.wrapT = wrapT; + return this.convert( color, sourceColorSpace, this.workingColorSpace ); - this.magFilter = magFilter; - this.minFilter = minFilter; + }, - this.anisotropy = anisotropy; +}; - this.format = format; - this.internalFormat = null; - this.type = type; +const _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 }; - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; +const _rgb = { r: 0, g: 0, b: 0 }; +const _hslA = { h: 0, s: 0, l: 0 }; +const _hslB = { h: 0, s: 0, l: 0 }; - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); +function hue2rgb( p, q, t ) { - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + 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; - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding; +} - this.version = 0; - this.onUpdate = null; +function toComponents( source, target ) { - } + target.r = source.r; + target.g = source.g; + target.b = source.b; - updateMatrix() { + return target; - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); +} - } +class Color { - clone() { + constructor( r, g, b ) { - return new this.constructor().copy( this ); + if ( g === undefined && b === undefined ) { - } + // r is THREE.Color, hex or string + return this.set( r ); - copy( source ) { + } - this.name = source.name; + return this.setRGB( r, g, b ); - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); + } - this.mapping = source.mapping; + set( value ) { - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + if ( value && value.isColor ) { - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + this.copy( value ); - this.anisotropy = source.anisotropy; + } else if ( typeof value === 'number' ) { - this.format = source.format; - this.internalFormat = source.internalFormat; - this.type = source.type; + this.setHex( value ); - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; + } else if ( typeof value === 'string' ) { - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); + this.setStyle( value ); - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; + } return this; } - toJSON( meta ) { - - const isRootObject = ( meta === undefined || typeof meta === 'string' ); - - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - - return meta.textures[ this.uuid ]; - - } - - const output = { - - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, - - uuid: this.uuid, - name: this.name, + setScalar( scalar ) { - mapping: this.mapping, + this.r = scalar; + this.g = scalar; + this.b = scalar; - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, + return this; - wrap: [ this.wrapS, this.wrapT ], + } - format: this.format, - type: this.type, - encoding: this.encoding, + setHex( hex, colorSpace = SRGBColorSpace ) { - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, + hex = Math.floor( hex ); - flipY: this.flipY, + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment + ColorManagement.toWorkingColorSpace( this, colorSpace ); - }; + return this; - if ( this.image !== undefined ) { + } - // TODO: Move to THREE.Image + setRGB( r, g, b, colorSpace = LinearSRGBColorSpace ) { - const image = this.image; + this.r = r; + this.g = g; + this.b = b; - if ( image.uuid === undefined ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - image.uuid = generateUUID(); // UGH + return this; - } + } - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { + setHSL( h, s, l, colorSpace = LinearSRGBColorSpace ) { - let url; + // h,s,l ranges are in 0.0 - 1.0 + h = euclideanModulo( h, 1 ); + s = clamp( s, 0, 1 ); + l = clamp( l, 0, 1 ); - if ( Array.isArray( image ) ) { + if ( s === 0 ) { - // process array of images e.g. CubeTexture + this.r = this.g = this.b = l; - url = []; + } else { - for ( let i = 0, l = image.length; i < l; i ++ ) { + const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + const q = ( 2 * l ) - p; - // check cube texture with data textures + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); - if ( image[ i ].isDataTexture ) { + } - url.push( serializeImage( image[ i ].image ) ); + ColorManagement.toWorkingColorSpace( this, colorSpace ); - } else { + return this; - url.push( serializeImage( image[ i ] ) ); + } - } + setStyle( style, colorSpace = SRGBColorSpace ) { - } + function handleAlpha( string ) { - } else { + if ( string === undefined ) return; - // process single image + if ( parseFloat( string ) < 1 ) { - url = serializeImage( image ); + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); - } + } - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; + } - } - output.image = image.uuid; + let m; - } + if ( m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec( style ) ) { - if ( ! isRootObject ) { + // rgb / hsl - meta.textures[ this.uuid ] = output; + let color; + const name = m[ 1 ]; + const components = m[ 2 ]; - } + switch ( name ) { - return output; + case 'rgb': + case 'rgba': - } + if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - dispose() { + // 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; - this.dispatchEvent( { type: 'dispose' } ); + ColorManagement.toWorkingColorSpace( this, colorSpace ); - } + handleAlpha( color[ 4 ] ); - transformUv( uv ) { + return this; - if ( this.mapping !== UVMapping ) return uv; + } - uv.applyMatrix3( this.matrix ); + if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - if ( uv.x < 0 || uv.x > 1 ) { + // 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; - switch ( this.wrapS ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - case RepeatWrapping: + handleAlpha( color[ 4 ] ); - uv.x = uv.x - Math.floor( uv.x ); - break; + return this; - case ClampToEdgeWrapping: + } - uv.x = uv.x < 0 ? 0 : 1; break; - case MirroredRepeatWrapping: + case 'hsl': + case 'hsla': - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - uv.x = Math.ceil( uv.x ) - uv.x; + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) + const h = parseFloat( color[ 1 ] ) / 360; + const s = parseInt( color[ 2 ], 10 ) / 100; + const l = parseInt( color[ 3 ], 10 ) / 100; - } else { + handleAlpha( color[ 4 ] ); - uv.x = uv.x - Math.floor( uv.x ); + return this.setHSL( h, s, l, colorSpace ); } @@ -3103,819 +3162,695 @@ class Texture extends EventDispatcher$1 { } - } - - if ( uv.y < 0 || uv.y > 1 ) { - - switch ( this.wrapT ) { - - case RepeatWrapping: + } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { - uv.y = uv.y - Math.floor( uv.y ); - break; + // hex color - case ClampToEdgeWrapping: + const hex = m[ 1 ]; + const size = hex.length; - uv.y = uv.y < 0 ? 0 : 1; - break; + if ( size === 3 ) { - case MirroredRepeatWrapping: + // #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; - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - uv.y = Math.ceil( uv.y ) - uv.y; + return this; - } else { + } else if ( size === 6 ) { - uv.y = uv.y - Math.floor( uv.y ); + // #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; - } + ColorManagement.toWorkingColorSpace( this, colorSpace ); - break; + return this; } } - if ( this.flipY ) { + if ( style && style.length > 0 ) { - uv.y = 1 - uv.y; + return this.setColorName( style, colorSpace ); } - return uv; - - } - - set needsUpdate( value ) { - - if ( value === true ) this.version ++; + return this; } -} - -Texture.DEFAULT_IMAGE = undefined; -Texture.DEFAULT_MAPPING = UVMapping; - -Texture.prototype.isTexture = true; - -function serializeImage( image ) { - - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - - // default images - - return ImageUtils.getDataURL( image ); - - } else { + setColorName( style, colorSpace = SRGBColorSpace ) { - if ( image.data ) { + // color keywords + const hex = _colorKeywords[ style.toLowerCase() ]; - // images of DataTexture + if ( hex !== undefined ) { - return { - data: Array.prototype.slice.call( image.data ), - width: image.width, - height: image.height, - type: image.data.constructor.name - }; + // red + this.setHex( hex, colorSpace ); } else { - console.warn( 'THREE.Texture: Unable to serialize Texture.' ); - return {}; + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); } - } - -} - -class Vector4 { - - constructor( x = 0, y = 0, z = 0, w = 1 ) { - - this.x = x; - this.y = y; - this.z = z; - this.w = w; + return this; } - get width() { + clone() { - return this.z; + return new this.constructor( this.r, this.g, this.b ); } - set width( value ) { - - this.z = value; - - } + copy( color ) { - get height() { + this.r = color.r; + this.g = color.g; + this.b = color.b; - return this.w; + return this; } - set height( value ) { + copySRGBToLinear( color ) { - this.w = value; + this.r = SRGBToLinear( color.r ); + this.g = SRGBToLinear( color.g ); + this.b = SRGBToLinear( color.b ); + + return this; } - set( x, y, z, w ) { + copyLinearToSRGB( color ) { - this.x = x; - this.y = y; - this.z = z; - this.w = w; + this.r = LinearToSRGB( color.r ); + this.g = LinearToSRGB( color.g ); + this.b = LinearToSRGB( color.b ); return this; } - setScalar( scalar ) { + convertSRGBToLinear() { - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; + this.copySRGBToLinear( this ); return this; } - setX( x ) { + convertLinearToSRGB() { - this.x = x; + this.copyLinearToSRGB( this ); return this; } - setY( y ) { + getHex( colorSpace = SRGBColorSpace ) { - this.y = y; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - return this; + return clamp( _rgb.r * 255, 0, 255 ) << 16 ^ clamp( _rgb.g * 255, 0, 255 ) << 8 ^ clamp( _rgb.b * 255, 0, 255 ) << 0; } - setZ( z ) { - - this.z = z; + getHexString( colorSpace = SRGBColorSpace ) { - return this; + return ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); } - setW( w ) { + getHSL( target, colorSpace = LinearSRGBColorSpace ) { - this.w = w; + // h,s,l ranges are in 0.0 - 1.0 - return this; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - } + const r = _rgb.r, g = _rgb.g, b = _rgb.b; - setComponent( index, value ) { + const max = Math.max( r, g, b ); + const min = Math.min( r, g, b ); - switch ( index ) { + let hue, saturation; + const lightness = ( min + max ) / 2.0; - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); + if ( min === max ) { - } + hue = 0; + saturation = 0; - return this; + } else { - } + const delta = max - min; - getComponent( index ) { + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - switch ( index ) { + switch ( max ) { - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); + 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; - clone() { + } - return new this.constructor( this.x, this.y, this.z, this.w ); + target.h = hue; + target.s = saturation; + target.l = lightness; + + return target; } - copy( v ) { + getRGB( target, colorSpace = LinearSRGBColorSpace ) { - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - return this; + target.r = _rgb.r; + target.g = _rgb.g; + target.b = _rgb.b; + + return target; } - add( v, w ) { + getStyle( colorSpace = SRGBColorSpace ) { - if ( w !== undefined ) { + ColorManagement.fromWorkingColorSpace( toComponents( this, _rgb ), colorSpace ); - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + if ( colorSpace !== SRGBColorSpace ) { - } + // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). + return `color(${ colorSpace } ${ _rgb.r } ${ _rgb.g } ${ _rgb.b })`; - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; + } - return this; + return `rgb(${( _rgb.r * 255 ) | 0},${( _rgb.g * 255 ) | 0},${( _rgb.b * 255 ) | 0})`; } - addScalar( s ) { + offsetHSL( h, s, l ) { - this.x += s; - this.y += s; - this.z += s; - this.w += s; + this.getHSL( _hslA ); + + _hslA.h += h; _hslA.s += s; _hslA.l += l; + + this.setHSL( _hslA.h, _hslA.s, _hslA.l ); return this; } - addVectors( a, b ) { + add( color ) { - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; + this.r += color.r; + this.g += color.g; + this.b += color.b; return this; } - addScaledVector( v, s ) { + addColors( color1, color2 ) { - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; return this; } - sub( v, w ) { - - if ( w !== undefined ) { - - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); - - } + addScalar( s ) { - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; + this.r += s; + this.g += s; + this.b += s; return this; } - subScalar( s ) { + sub( color ) { - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; + 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; } - subVectors( a, b ) { + multiply( color ) { - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; return this; } - multiply( v ) { + multiplyScalar( s ) { - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; - this.w *= v.w; + this.r *= s; + this.g *= s; + this.b *= s; return this; } - multiplyScalar( scalar ) { + lerp( color, alpha ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; return this; } - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z, w = this.w; - const e = m.elements; + lerpColors( color1, color2, alpha ) { - 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; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + this.r = color1.r + ( color2.r - color1.r ) * alpha; + this.g = color1.g + ( color2.g - color1.g ) * alpha; + this.b = color1.b + ( color2.b - color1.b ) * alpha; return this; } - divideScalar( scalar ) { - - return this.multiplyScalar( 1 / scalar ); - - } + lerpHSL( color, alpha ) { - setAxisAngleFromQuaternion( q ) { + this.getHSL( _hslA ); + color.getHSL( _hslB ); - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + const h = lerp( _hslA.h, _hslB.h, alpha ); + const s = lerp( _hslA.s, _hslB.s, alpha ); + const l = lerp( _hslA.l, _hslB.l, alpha ); - // q is assumed to be normalized + this.setHSL( h, s, l ); - this.w = 2 * Math.acos( q.w ); + return this; - const s = Math.sqrt( 1 - q.w * q.w ); + } - if ( s < 0.0001 ) { + equals( c ) { - this.x = 1; - this.y = 0; - this.z = 0; + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - } else { + } - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; + fromArray( array, offset = 0 ) { - } + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; return this; } - setAxisAngleFromRotationMatrix( m ) { + toArray( array = [], offset = 0 ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + array[ offset ] = this.r; + array[ offset + 1 ] = this.g; + array[ offset + 2 ] = this.b; - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + return array; - let angle, x, y, z; // variables for result - const epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + } - te = m.elements, + fromBufferAttribute( attribute, index ) { - 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 ]; + this.r = attribute.getX( index ); + this.g = attribute.getY( index ); + this.b = attribute.getZ( index ); - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { + if ( attribute.normalized === true ) { - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms + // assuming Uint8Array - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + this.r /= 255; + this.g /= 255; + this.b /= 255; - // this singularity is identity matrix so angle = 0 + } - this.set( 1, 0, 0, 0 ); + return this; - return this; // zero angle, arbitrary axis + } - } + toJSON() { - // otherwise this singularity is angle = 180 + return this.getHex(); - angle = Math.PI; + } - const xx = ( m11 + 1 ) / 2; - const yy = ( m22 + 1 ) / 2; - const zz = ( m33 + 1 ) / 2; - const xy = ( m12 + m21 ) / 4; - const xz = ( m13 + m31 ) / 4; - const yz = ( m23 + m32 ) / 4; +} - if ( ( xx > yy ) && ( xx > zz ) ) { +Color.NAMES = _colorKeywords; - // m11 is the largest diagonal term +Color.prototype.isColor = true; +Color.prototype.r = 1; +Color.prototype.g = 1; +Color.prototype.b = 1; - if ( xx < epsilon ) { +let _canvas; - x = 0; - y = 0.707106781; - z = 0.707106781; +class ImageUtils { - } else { + static getDataURL( image ) { - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; + if ( /^data:/i.test( image.src ) ) { - } + return image.src; - } else if ( yy > zz ) { + } - // m22 is the largest diagonal term + if ( typeof HTMLCanvasElement == 'undefined' ) { - if ( yy < epsilon ) { + return image.src; - x = 0.707106781; - y = 0; - z = 0.707106781; + } - } else { + let canvas; - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; + if ( image instanceof HTMLCanvasElement ) { - } + canvas = image; - } else { + } else { - // m33 is the largest diagonal term so base result on this + if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' ); - if ( zz < epsilon ) { + _canvas.width = image.width; + _canvas.height = image.height; - x = 0.707106781; - y = 0.707106781; - z = 0; + const context = _canvas.getContext( '2d' ); - } else { + if ( image instanceof ImageData ) { - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; + context.putImageData( image, 0, 0 ); - } + } else { - } + context.drawImage( image, 0, 0, image.width, image.height ); - this.set( x, y, z, angle ); + } - return this; // return 180 deg rotation + canvas = _canvas; } - // as we have reached here there are no singularities so we can handle normally + if ( canvas.width > 2048 || canvas.height > 2048 ) { - let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); - if ( Math.abs( s ) < 0.001 ) s = 1; + return canvas.toDataURL( 'image/jpeg', 0.6 ); - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case + } else { - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + return canvas.toDataURL( 'image/png' ); - return this; + } } - min( v ) { + static sRGBToLinear( image ) { - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - return this; + const canvas = createElementNS( 'canvas' ); - } + canvas.width = image.width; + canvas.height = image.height; - max( v ) { + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height ); - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); + const imageData = context.getImageData( 0, 0, image.width, image.height ); + const data = imageData.data; - return this; + for ( let i = 0; i < data.length; i ++ ) { - } + data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; - clamp( min, max ) { + } - // assumes min < max, componentwise + context.putImageData( imageData, 0, 0 ); - 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 ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); + return canvas; - return this; + } else if ( image.data ) { - } + const data = image.data.slice( 0 ); - clampScalar( minVal, maxVal ) { + for ( let i = 0; i < data.length; i ++ ) { - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); + if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { - return this; + data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); - } + } else { - clampLength( min, max ) { + // assuming float - const length = this.length(); + data[ i ] = SRGBToLinear( data[ i ] ); - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + } - } + } - floor() { + return { + data: data, + width: image.width, + height: image.height + }; - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); + } else { - return this; + console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); + return image; - } + } - ceil() { + } - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); +} - return this; +class Source { - } + constructor( data = null ) { - round() { + this.uuid = generateUUID(); - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); + this.data = data; - return this; + this.version = 0; } - roundToZero() { - - 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 ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + set needsUpdate( value ) { - return this; + if ( value === true ) this.version ++; } - negate() { + toJSON( meta ) { - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - return this; + if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { - } + return meta.images[ this.uuid ]; - dot( v ) { + } - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + const output = { + uuid: this.uuid, + url: '' + }; - } + const data = this.data; - lengthSq() { + if ( data !== null ) { - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + let url; - } + if ( Array.isArray( data ) ) { - length() { + // cube texture - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + url = []; - } + for ( let i = 0, l = data.length; i < l; i ++ ) { - manhattanLength() { + if ( data[ i ].isDataTexture ) { - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + url.push( serializeImage( data[ i ].image ) ); - } + } else { - normalize() { + url.push( serializeImage( data[ i ] ) ); - return this.divideScalar( this.length() || 1 ); + } - } + } - setLength( length ) { + } else { - return this.normalize().multiplyScalar( length ); + // texture - } + url = serializeImage( data ); - lerp( v, alpha ) { + } - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; + output.url = url; - return this; + } - } + if ( ! isRootObject ) { - lerpVectors( v1, v2, alpha ) { + meta.images[ this.uuid ] = output; - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; - this.w = v1.w + ( v2.w - v1.w ) * alpha; + } - return this; + return output; } - equals( v ) { +} - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); +function serializeImage( image ) { - } + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - fromArray( array, offset = 0 ) { + // default images - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; + return ImageUtils.getDataURL( image ); - return this; + } else { - } + if ( image.data ) { - toArray( array = [], offset = 0 ) { + // images of DataTexture - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; + return { + data: Array.prototype.slice.call( image.data ), + width: image.width, + height: image.height, + type: image.data.constructor.name + }; - return array; + } else { - } + console.warn( 'THREE.Texture: Unable to serialize Texture.' ); + return {}; - fromBufferAttribute( attribute, index, offset ) { + } - if ( offset !== undefined ) { + } - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); +} - } +Source.prototype.isSource = true; - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); +let textureId = 0; - return this; +class Texture extends EventDispatcher$1 { - } + constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = 1, encoding = LinearEncoding ) { - random() { + super(); - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); - this.w = Math.random(); + Object.defineProperty( this, 'id', { value: textureId ++ } ); - return this; + this.uuid = generateUUID(); - } + this.name = ''; -} + this.source = new Source( image ); + this.mipmaps = []; -Vector4.prototype.isVector4 = true; + this.mapping = mapping; -/* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers -*/ -class WebGLRenderTarget extends EventDispatcher$1 { + this.wrapS = wrapS; + this.wrapT = wrapT; - constructor( width, height, options ) { + this.magFilter = magFilter; + this.minFilter = minFilter; - super(); + this.anisotropy = anisotropy; - this.width = width; - this.height = height; - this.depth = 1; + this.format = format; + this.internalFormat = null; + this.type = type; - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; + this.offset = new Vector2( 0, 0 ); + this.repeat = new Vector2( 1, 1 ); + this.center = new Vector2( 0, 0 ); + this.rotation = 0; - this.viewport = new Vector4( 0, 0, width, height ); + this.matrixAutoUpdate = true; + this.matrix = new Matrix3(); - options = options || {}; + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + this.encoding = encoding; - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; - this.texture.image.depth = 1; + this.userData = {}; - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; + this.version = 0; + this.onUpdate = null; - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; + this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not + this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } - setTexture( texture ) { - - texture.image = { - width: this.width, - height: this.height, - depth: this.depth - }; + get image() { - this.texture = texture; + return this.source.data; } - setSize( width, height, depth = 1 ) { - - if ( this.width !== width || this.height !== height || this.depth !== depth ) { - - this.width = width; - this.height = height; - this.depth = depth; + set image( value ) { - this.texture.image.width = width; - this.texture.image.height = height; - this.texture.image.depth = depth; + this.source.data = value; - this.dispose(); + } - } + updateMatrix() { - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); + this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); } @@ -3927,2711 +3862,2693 @@ class WebGLRenderTarget extends EventDispatcher$1 { copy( source ) { - this.width = source.width; - this.height = source.height; - this.depth = source.depth; + this.name = source.name; - this.viewport.copy( source.viewport ); + this.source = source.source; + this.mipmaps = source.mipmaps.slice( 0 ); - this.texture = source.texture.clone(); + this.mapping = source.mapping; - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; - return this; + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; - } + this.anisotropy = source.anisotropy; - dispose() { + this.format = source.format; + this.internalFormat = source.internalFormat; + this.type = source.type; - this.dispatchEvent( { type: 'dispose' } ); + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + this.center.copy( source.center ); + this.rotation = source.rotation; + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrix.copy( source.matrix ); + + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + this.needsUpdate = true; + + return this; } -} + toJSON( meta ) { -WebGLRenderTarget.prototype.isWebGLRenderTarget = true; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); -class WebGLMultisampleRenderTarget extends WebGLRenderTarget { + if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - constructor( width, height, options ) { + return meta.textures[ this.uuid ]; - super( width, height, options ); + } - this.samples = 4; + const output = { - } + metadata: { + version: 4.5, + type: 'Texture', + generator: 'Texture.toJSON' + }, - copy( source ) { + uuid: this.uuid, + name: this.name, - super.copy.call( this, source ); + image: this.source.toJSON( meta ).uuid, - this.samples = source.samples; + mapping: this.mapping, - return this; + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + center: [ this.center.x, this.center.y ], + rotation: this.rotation, - } + wrap: [ this.wrapS, this.wrapT ], -} + format: this.format, + type: this.type, + encoding: this.encoding, -WebGLMultisampleRenderTarget.prototype.isWebGLMultisampleRenderTarget = true; + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, -class Quaternion { + flipY: this.flipY, - constructor( x = 0, y = 0, z = 0, w = 1 ) { + premultiplyAlpha: this.premultiplyAlpha, + unpackAlignment: this.unpackAlignment - this._x = x; - this._y = y; - this._z = z; - this._w = w; + }; - } + if ( JSON.stringify( this.userData ) !== '{}' ) output.userData = this.userData; - static slerp( qa, qb, qm, t ) { + if ( ! isRootObject ) { - console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); - return qm.slerpQuaternions( qa, qb, t ); + meta.textures[ this.uuid ] = output; - } + } - static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { + return output; - // fuzz-free, array-based Quaternion SLERP operation + } - let x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ]; + dispose() { - const x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; + this.dispatchEvent( { type: 'dispose' } ); - if ( t === 0 ) { + } - dst[ dstOffset + 0 ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - return; + transformUv( uv ) { - } + if ( this.mapping !== UVMapping ) return uv; - if ( t === 1 ) { + uv.applyMatrix3( this.matrix ); - dst[ dstOffset + 0 ] = x1; - dst[ dstOffset + 1 ] = y1; - dst[ dstOffset + 2 ] = z1; - dst[ dstOffset + 3 ] = w1; - return; + if ( uv.x < 0 || uv.x > 1 ) { - } + switch ( this.wrapS ) { - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + case RepeatWrapping: - let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; + uv.x = uv.x - Math.floor( uv.x ); + break; - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { + case ClampToEdgeWrapping: - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); + uv.x = uv.x < 0 ? 0 : 1; + break; - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; + case MirroredRepeatWrapping: - } + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - const tDir = t * dir; + uv.x = Math.ceil( uv.x ) - uv.x; - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; + } else { - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { + uv.x = uv.x - Math.floor( uv.x ); - const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); + } - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; + break; } } - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; + if ( uv.y < 0 || uv.y > 1 ) { - } + switch ( this.wrapT ) { - static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { + case RepeatWrapping: - const x0 = src0[ srcOffset0 ]; - const y0 = src0[ srcOffset0 + 1 ]; - const z0 = src0[ srcOffset0 + 2 ]; - const w0 = src0[ srcOffset0 + 3 ]; + uv.y = uv.y - Math.floor( uv.y ); + break; - const x1 = src1[ srcOffset1 ]; - const y1 = src1[ srcOffset1 + 1 ]; - const z1 = src1[ srcOffset1 + 2 ]; - const w1 = src1[ srcOffset1 + 3 ]; + case ClampToEdgeWrapping: - dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; - dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; - dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; - dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; + uv.y = uv.y < 0 ? 0 : 1; + break; - return dst; + case MirroredRepeatWrapping: - } + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - get x() { + uv.y = Math.ceil( uv.y ) - uv.y; - return this._x; + } else { - } + uv.y = uv.y - Math.floor( uv.y ); - set x( value ) { + } - this._x = value; - this._onChangeCallback(); + break; - } + } - get y() { + } - return this._y; + if ( this.flipY ) { - } + uv.y = 1 - uv.y; - set y( value ) { + } - this._y = value; - this._onChangeCallback(); + return uv; } - get z() { + set needsUpdate( value ) { - return this._z; + if ( value === true ) { + + this.version ++; + this.source.needsUpdate = true; + + } } - set z( value ) { +} - this._z = value; - this._onChangeCallback(); +Texture.DEFAULT_IMAGE = null; +Texture.DEFAULT_MAPPING = UVMapping; - } +Texture.prototype.isTexture = true; - get w() { +class Vector4 { - return this._w; + constructor( x = 0, y = 0, z = 0, w = 1 ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; } - set w( value ) { + get width() { - this._w = value; - this._onChangeCallback(); + return this.z; } - set( x, y, z, w ) { + set width( value ) { - this._x = x; - this._y = y; - this._z = z; - this._w = w; + this.z = value; - this._onChangeCallback(); + } - return this; + get height() { + + return this.w; } - clone() { + set height( value ) { - return new this.constructor( this._x, this._y, this._z, this._w ); + this.w = value; } - copy( quaternion ) { - - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; + set( x, y, z, w ) { - this._onChangeCallback(); + this.x = x; + this.y = y; + this.z = z; + this.w = w; return this; } - setFromEuler( euler, update ) { - - if ( ! ( euler && euler.isEuler ) ) { + setScalar( scalar ) { - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; - } + return this; - const 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 + setX( x ) { - const cos = Math.cos; - const sin = Math.sin; + this.x = x; - const c1 = cos( x / 2 ); - const c2 = cos( y / 2 ); - const c3 = cos( z / 2 ); + return this; - const s1 = sin( x / 2 ); - const s2 = sin( y / 2 ); - const s3 = sin( z / 2 ); + } - switch ( order ) { + setY( y ) { - case '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; - break; + this.y = y; - case '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; - break; + return this; - case '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; - break; + } - case '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; - break; + setZ( z ) { - case '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; - break; + this.z = z; - case '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; - break; + return this; - default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); + } - } + setW( w ) { - if ( update !== false ) this._onChangeCallback(); + this.w = w; return this; } - setFromAxisAngle( axis, angle ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - - // assumes axis is normalized + setComponent( index, value ) { - const halfAngle = angle / 2, s = Math.sin( halfAngle ); + switch ( index ) { - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); - this._onChangeCallback(); + } return this; } - setFromRotationMatrix( 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) - - const te = m.elements, + getComponent( index ) { - 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 ], + switch ( index ) { - trace = m11 + m22 + m33; + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); - if ( trace > 0 ) { + } - const 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; + clone() { - } else if ( m11 > m22 && m11 > m33 ) { + return new this.constructor( this.x, this.y, this.z, this.w ); - const 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; + copy( v ) { - } else if ( m22 > m33 ) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; - const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + return this; - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; + } - } else { + add( v, w ) { - const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + if ( w !== undefined ) { - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); } - this._onChangeCallback(); + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; return this; } - setFromUnitVectors( vFrom, vTo ) { + addScalar( s ) { - // assumes direction vectors vFrom and vTo are normalized + this.x += s; + this.y += s; + this.z += s; + this.w += s; - let r = vFrom.dot( vTo ) + 1; + return this; - if ( r < Number.EPSILON ) { + } - // vFrom and vTo point in opposite directions - - r = 0; + addVectors( a, b ) { - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; + return this; - } else { + } - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; + addScaledVector( v, s ) { - } + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; - } else { + return this; - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 + } - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; + sub( v, w ) { - } + if ( w !== undefined ) { - return this.normalize(); + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); - } + } - angleTo( q ) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; - return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); + return this; } - rotateTowards( q, step ) { - - const angle = this.angleTo( q ); - - if ( angle === 0 ) return this; - - const t = Math.min( 1, step / angle ); + subScalar( s ) { - this.slerp( q, t ); + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; return this; } - identity() { + subVectors( a, b ) { - return this.set( 0, 0, 0, 1 ); + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; } - invert() { + multiply( v ) { - // quaternion is assumed to have unit length + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + this.w *= v.w; - return this.conjugate(); + return this; } - conjugate() { - - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; + multiplyScalar( scalar ) { - this._onChangeCallback(); + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; return this; } - dot( v ) { + applyMatrix4( m ) { - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + const x = this.x, y = this.y, z = this.z, w = this.w; + const e = m.elements; + + 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; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; } - lengthSq() { + divideScalar( scalar ) { - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + return this.multiplyScalar( 1 / scalar ); } - length() { + setAxisAngleFromQuaternion( q ) { - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - } + // q is assumed to be normalized - normalize() { + this.w = 2 * Math.acos( q.w ); - let l = this.length(); + const s = Math.sqrt( 1 - q.w * q.w ); - if ( l === 0 ) { + if ( s < 0.0001 ) { - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; + this.x = 1; + this.y = 0; + this.z = 0; } 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.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; } - this._onChangeCallback(); - return this; } - multiply( q, p ) { + setAxisAngleFromRotationMatrix( m ) { - if ( p !== undefined ) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - } + let angle, x, y, z; // variables for result + const epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - return this.multiplyQuaternions( this, q ); + 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 ]; - premultiply( q ) { + if ( ( Math.abs( m12 - m21 ) < epsilon ) && + ( Math.abs( m13 - m31 ) < epsilon ) && + ( Math.abs( m23 - m32 ) < epsilon ) ) { - return this.multiplyQuaternions( q, this ); + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms - } + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && + ( Math.abs( m13 + m31 ) < epsilon2 ) && + ( Math.abs( m23 + m32 ) < epsilon2 ) && + ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - multiplyQuaternions( a, b ) { + // this singularity is identity matrix so angle = 0 - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + this.set( 1, 0, 0, 0 ); - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + return this; // zero angle, arbitrary axis - 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(); + // otherwise this singularity is angle = 180 - return this; + angle = Math.PI; - } + const xx = ( m11 + 1 ) / 2; + const yy = ( m22 + 1 ) / 2; + const zz = ( m33 + 1 ) / 2; + const xy = ( m12 + m21 ) / 4; + const xz = ( m13 + m31 ) / 4; + const yz = ( m23 + m32 ) / 4; - slerp( qb, t ) { + if ( ( xx > yy ) && ( xx > zz ) ) { - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); + // m11 is the largest diagonal term - const x = this._x, y = this._y, z = this._z, w = this._w; + if ( xx < epsilon ) { - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + x = 0; + y = 0.707106781; + z = 0.707106781; - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + } else { - if ( cosHalfTheta < 0 ) { + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; + } - cosHalfTheta = - cosHalfTheta; + } else if ( yy > zz ) { - } else { + // m22 is the largest diagonal term - this.copy( qb ); + if ( yy < epsilon ) { - } + x = 0.707106781; + y = 0; + z = 0.707106781; - if ( cosHalfTheta >= 1.0 ) { + } else { - this._w = w; - this._x = x; - this._y = y; - this._z = z; + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; - return this; + } - } + } else { - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + // m33 is the largest diagonal term so base result on this - if ( sqrSinHalfTheta <= Number.EPSILON ) { + if ( zz < epsilon ) { - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; + x = 0.707106781; + y = 0.707106781; + z = 0; - this.normalize(); - this._onChangeCallback(); + } else { - return this; + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; - } + } - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const 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.set( x, y, z, angle ); - this._onChangeCallback(); + return this; // return 180 deg rotation - return this; + } - } + // as we have reached here there are no singularities so we can handle normally - slerpQuaternions( qa, qb, t ) { + let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - this.copy( qa ).slerp( qb, t ); + if ( Math.abs( s ) < 0.001 ) s = 1; - } + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case - equals( quaternion ) { + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + return this; } - fromArray( array, offset = 0 ) { - - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; + min( v ) { - this._onChangeCallback(); + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); return this; } - toArray( array = [], offset = 0 ) { + max( v ) { - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); - return array; + return this; } - fromBufferAttribute( attribute, index ) { + clamp( min, max ) { - this._x = attribute.getX( index ); - this._y = attribute.getY( index ); - this._z = attribute.getZ( index ); - this._w = attribute.getW( index ); + // 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 ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; } - _onChange( callback ) { + clampScalar( minVal, maxVal ) { - this._onChangeCallback = callback; + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); + this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); return this; } - _onChangeCallback() {} - -} - -Quaternion.prototype.isQuaternion = true; - -class Vector3 { + clampLength( min, max ) { - constructor( x = 0, y = 0, z = 0 ) { + const length = this.length(); - this.x = x; - this.y = y; - this.z = z; + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } - set( x, y, z ) { - - if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) + floor() { - this.x = x; - this.y = y; - this.z = z; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); return this; } - setScalar( scalar ) { + ceil() { - this.x = scalar; - this.y = scalar; - this.z = scalar; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); return this; } - setX( x ) { + round() { - this.x = x; + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); return this; } - setY( y ) { + roundToZero() { - this.y = y; + 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 ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; } - setZ( z ) { + negate() { - this.z = z; + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; return this; } - setComponent( index, value ) { + dot( v ) { - switch ( index ) { + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - 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 ); + } - } + lengthSq() { - return this; + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } - getComponent( 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 ); + length() { - } + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); } - clone() { + manhattanLength() { - return new this.constructor( this.x, this.y, this.z ); + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); } - copy( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; + normalize() { - return this; + return this.divideScalar( this.length() || 1 ); } - add( v, w ) { + setLength( length ) { - if ( w !== undefined ) { + return this.normalize().multiplyScalar( length ); - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + } - } + lerp( v, alpha ) { - this.x += v.x; - this.y += v.y; - this.z += v.z; + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; return this; } - addScalar( s ) { + lerpVectors( v1, v2, alpha ) { - this.x += s; - this.y += s; - this.z += s; + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; + this.w = v1.w + ( v2.w - v1.w ) * alpha; return this; } - addVectors( a, b ) { - - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; + equals( v ) { - return this; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); } - addScaledVector( v, s ) { + fromArray( array, offset = 0 ) { - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; return this; } - sub( v, w ) { + toArray( array = [], offset = 0 ) { - if ( w !== undefined ) { + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + return array; - } + } - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; + fromBufferAttribute( attribute, index, offset ) { - return this; + if ( offset !== undefined ) { - } + console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - subScalar( s ) { + } - this.x -= s; - this.y -= s; - this.z -= s; + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); + this.w = attribute.getW( index ); return this; } - subVectors( a, b ) { + random() { - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); + this.w = Math.random(); return this; } - multiply( v, w ) { + *[ Symbol.iterator ]() { - if ( w !== undefined ) { + yield this.x; + yield this.y; + yield this.z; + yield this.w; - console.warn( 'THREE.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; +Vector4.prototype.isVector4 = true; - return this; - - } - - multiplyScalar( scalar ) { - - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; +/* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers +*/ +class WebGLRenderTarget extends EventDispatcher$1 { - return this; + constructor( width, height, options = {} ) { - } + super(); - multiplyVectors( a, b ) { + this.width = width; + this.height = height; + this.depth = 1; - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; + this.scissor = new Vector4( 0, 0, width, height ); + this.scissorTest = false; - return this; + this.viewport = new Vector4( 0, 0, width, height ); - } + const image = { width: width, height: height, depth: 1 }; - applyEuler( euler ) { + this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; - if ( ! ( euler && euler.isEuler ) ) { + this.texture.flipY = false; + this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; + this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; - } + this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); + this.samples = options.samples !== undefined ? options.samples : 0; } - applyAxisAngle( axis, angle ) { + setSize( width, height, depth = 1 ) { - return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - } + this.width = width; + this.height = height; + this.depth = depth; - applyMatrix3( m ) { + this.texture.image.width = width; + this.texture.image.height = height; + this.texture.image.depth = depth; - const x = this.x, y = this.y, z = this.z; - const e = m.elements; + this.dispose(); - 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; + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); } - applyNormalMatrix( m ) { + clone() { - return this.applyMatrix3( m ).normalize(); + return new this.constructor().copy( this ); } - applyMatrix4( m ) { - - const x = this.x, y = this.y, z = this.z; - const e = m.elements; - - const 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; + copy( source ) { - return this; + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - } + this.viewport.copy( source.viewport ); - applyQuaternion( q ) { + this.texture = source.texture.clone(); + this.texture.isRenderTargetTexture = true; - const x = this.x, y = this.y, z = this.z; - const qx = q.x, qy = q.y, qz = q.z, qw = q.w; + // ensure image object is not shared, see #20328 - // calculate quat * vector + this.texture.image = Object.assign( {}, source.texture.image ); - const ix = qw * x + qy * z - qz * y; - const iy = qw * y + qz * x - qx * z; - const iz = qw * z + qx * y - qy * x; - const iw = - qx * x - qy * y - qz * z; + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - // calculate result * inverse quat + if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); - 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; + this.samples = source.samples; return this; } - project( camera ) { + dispose() { - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); + this.dispatchEvent( { type: 'dispose' } ); } - unproject( camera ) { +} - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); +WebGLRenderTarget.prototype.isWebGLRenderTarget = true; - } +class DataArrayTexture extends Texture { - transformDirection( m ) { + constructor( data = null, width = 1, height = 1, depth = 1 ) { - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction + super( null ); - const x = this.x, y = this.y, z = this.z; - const e = m.elements; + this.image = { data, width, height, depth }; - 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; + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - return this.normalize(); + this.wrapR = ClampToEdgeWrapping; + + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; } - divide( v ) { +} - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; +DataArrayTexture.prototype.isDataArrayTexture = true; - return this; +class WebGLArrayRenderTarget extends WebGLRenderTarget { - } + constructor( width, height, depth ) { - divideScalar( scalar ) { + super( width, height ); - return this.multiplyScalar( 1 / scalar ); + this.depth = depth; - } + this.texture = new DataArrayTexture( null, width, height, depth ); - min( v ) { + this.texture.isRenderTargetTexture = true; - 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; +} - } +WebGLArrayRenderTarget.prototype.isWebGLArrayRenderTarget = true; - max( v ) { +class Data3DTexture extends Texture { - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); + constructor( data = null, width = 1, height = 1, depth = 1 ) { - return this; + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // const texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 - } + super( null ); - clamp( min, max ) { + this.image = { data, width, height, depth }; - // assumes min < max, componentwise + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - 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 ) ); + this.wrapR = ClampToEdgeWrapping; - return this; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; } - clampScalar( minVal, maxVal ) { +} - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); +Data3DTexture.prototype.isData3DTexture = true; - return this; +class WebGL3DRenderTarget extends WebGLRenderTarget { - } + constructor( width, height, depth ) { - clampLength( min, max ) { + super( width, height ); - const length = this.length(); + this.depth = depth; - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + this.texture = new Data3DTexture( null, width, height, depth ); - } + this.texture.isRenderTargetTexture = true; - floor() { + } - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); +} - return this; +WebGL3DRenderTarget.prototype.isWebGL3DRenderTarget = true; - } +class WebGLMultipleRenderTargets extends WebGLRenderTarget { - ceil() { + constructor( width, height, count, options = {} ) { - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); + super( width, height, options ); - return this; + const texture = this.texture; - } + this.texture = []; - round() { + for ( let i = 0; i < count; i ++ ) { - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); + this.texture[ i ] = texture.clone(); + this.texture[ i ].isRenderTargetTexture = true; - return this; + } } - roundToZero() { + setSize( width, height, depth = 1 ) { - 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 ); + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - return this; + this.width = width; + this.height = height; + this.depth = depth; - } + for ( let i = 0, il = this.texture.length; i < il; i ++ ) { - negate() { + this.texture[ i ].image.width = width; + this.texture[ i ].image.height = height; + this.texture[ i ].image.depth = depth; - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; + } - return this; + this.dispose(); - } + } - dot( v ) { + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - return this.x * v.x + this.y * v.y + this.z * v.z; + return this; } - // TODO lengthSquared? - - lengthSq() { - - return this.x * this.x + this.y * this.y + this.z * this.z; + copy( source ) { - } + this.dispose(); - length() { + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + this.viewport.set( 0, 0, this.width, this.height ); + this.scissor.set( 0, 0, this.width, this.height ); - } + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; - manhattanLength() { + this.texture.length = 0; - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + for ( let i = 0, il = source.texture.length; i < il; i ++ ) { - } + this.texture[ i ] = source.texture[ i ].clone(); - normalize() { + } - return this.divideScalar( this.length() || 1 ); + return this; } - setLength( length ) { - - return this.normalize().multiplyScalar( length ); +} - } +WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true; - lerp( v, alpha ) { +class Quaternion { - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; + constructor( x = 0, y = 0, z = 0, w = 1 ) { - return this; + this._x = x; + this._y = y; + this._z = z; + this._w = w; } - lerpVectors( v1, v2, alpha ) { - - this.x = v1.x + ( v2.x - v1.x ) * alpha; - this.y = v1.y + ( v2.y - v1.y ) * alpha; - this.z = v1.z + ( v2.z - v1.z ) * alpha; + static slerp( qa, qb, qm, t ) { - return this; + console.warn( 'THREE.Quaternion: Static .slerp() has been deprecated. Use qm.slerpQuaternions( qa, qb, t ) instead.' ); + return qm.slerpQuaternions( qa, qb, t ); } - cross( v, w ) { + static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - if ( w !== undefined ) { + // fuzz-free, array-based Quaternion SLERP operation - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); + let x0 = src0[ srcOffset0 + 0 ], + y0 = src0[ srcOffset0 + 1 ], + z0 = src0[ srcOffset0 + 2 ], + w0 = src0[ srcOffset0 + 3 ]; - } + const x1 = src1[ srcOffset1 + 0 ], + y1 = src1[ srcOffset1 + 1 ], + z1 = src1[ srcOffset1 + 2 ], + w1 = src1[ srcOffset1 + 3 ]; - return this.crossVectors( this, v ); + if ( t === 0 ) { - } + dst[ dstOffset + 0 ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; + return; - crossVectors( a, b ) { + } - const ax = a.x, ay = a.y, az = a.z; - const bx = b.x, by = b.y, bz = b.z; + if ( t === 1 ) { - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; + dst[ dstOffset + 0 ] = x1; + dst[ dstOffset + 1 ] = y1; + dst[ dstOffset + 2 ] = z1; + dst[ dstOffset + 3 ] = w1; + return; - return this; + } - } + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - projectOnVector( v ) { + let s = 1 - t; + const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + dir = ( cos >= 0 ? 1 : - 1 ), + sqrSin = 1 - cos * cos; - const denominator = v.lengthSq(); + // Skip the Slerp for tiny steps to avoid numeric problems: + if ( sqrSin > Number.EPSILON ) { - if ( denominator === 0 ) return this.set( 0, 0, 0 ); + const sin = Math.sqrt( sqrSin ), + len = Math.atan2( sin, cos * dir ); - const scalar = v.dot( this ) / denominator; + s = Math.sin( s * len ) / sin; + t = Math.sin( t * len ) / sin; - return this.copy( v ).multiplyScalar( scalar ); + } - } + const tDir = t * dir; - projectOnPlane( planeNormal ) { + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; - _vector$c.copy( this ).projectOnVector( planeNormal ); + // Normalize in case we just did a lerp: + if ( s === 1 - t ) { - return this.sub( _vector$c ); + const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - } + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; - reflect( normal ) { + } - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length + } - return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + dst[ dstOffset ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; } - angleTo( v ) { - - const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); + static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - if ( denominator === 0 ) return Math.PI / 2; + const x0 = src0[ srcOffset0 ]; + const y0 = src0[ srcOffset0 + 1 ]; + const z0 = src0[ srcOffset0 + 2 ]; + const w0 = src0[ srcOffset0 + 3 ]; - const theta = this.dot( v ) / denominator; + const x1 = src1[ srcOffset1 ]; + const y1 = src1[ srcOffset1 + 1 ]; + const z1 = src1[ srcOffset1 + 2 ]; + const w1 = src1[ srcOffset1 + 3 ]; - // clamp, to handle numerical problems + dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; + dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; + dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; + dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - return Math.acos( clamp( theta, - 1, 1 ) ); + return dst; } - distanceTo( v ) { + get x() { - return Math.sqrt( this.distanceToSquared( v ) ); + return this._x; } - distanceToSquared( v ) { - - const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + set x( value ) { - return dx * dx + dy * dy + dz * dz; + this._x = value; + this._onChangeCallback(); } - manhattanDistanceTo( v ) { + get y() { - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); + return this._y; } - setFromSpherical( s ) { + set y( value ) { - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); + this._y = value; + this._onChangeCallback(); } - setFromSphericalCoords( radius, phi, theta ) { - - const sinPhiRadius = Math.sin( phi ) * radius; - - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); + get z() { - return this; + return this._z; } - setFromCylindrical( c ) { + set z( value ) { - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); + this._z = value; + this._onChangeCallback(); } - setFromCylindricalCoords( radius, theta, y ) { - - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); + get w() { - return this; + return this._w; } - setFromMatrixPosition( m ) { - - const e = m.elements; - - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; + set w( value ) { - return this; + this._w = value; + this._onChangeCallback(); } - setFromMatrixScale( m ) { + set( x, y, z, w ) { - const sx = this.setFromMatrixColumn( m, 0 ).length(); - const sy = this.setFromMatrixColumn( m, 1 ).length(); - const sz = this.setFromMatrixColumn( m, 2 ).length(); + this._x = x; + this._y = y; + this._z = z; + this._w = w; - this.x = sx; - this.y = sy; - this.z = sz; + this._onChangeCallback(); return this; } - setFromMatrixColumn( m, index ) { + clone() { - return this.fromArray( m.elements, index * 4 ); + return new this.constructor( this._x, this._y, this._z, this._w ); } - setFromMatrix3Column( m, index ) { - - return this.fromArray( m.elements, index * 3 ); + copy( quaternion ) { - } + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; - equals( v ) { + this._onChangeCallback(); - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + return this; } - fromArray( array, offset = 0 ) { + setFromEuler( euler, update ) { - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; + if ( ! ( euler && euler.isEuler ) ) { - return this; + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - } + } - toArray( array = [], offset = 0 ) { + const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m - return array; + const cos = Math.cos; + const sin = Math.sin; - } + const c1 = cos( x / 2 ); + const c2 = cos( y / 2 ); + const c3 = cos( z / 2 ); - fromBufferAttribute( attribute, index, offset ) { + const s1 = sin( x / 2 ); + const s2 = sin( y / 2 ); + const s3 = sin( z / 2 ); - if ( offset !== undefined ) { + switch ( order ) { - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); + case '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; + break; - } + case '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; + break; - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - - return this; + case '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; + break; - } + case '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; + break; - random() { + case '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; + break; - this.x = Math.random(); - this.y = Math.random(); - this.z = Math.random(); + case '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; + break; - return this; + default: + console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - } + } -} + if ( update !== false ) this._onChangeCallback(); -Vector3.prototype.isVector3 = true; + return this; -const _vector$c = /*@__PURE__*/ new Vector3(); -const _quaternion$4 = /*@__PURE__*/ new Quaternion(); + } -class Box3 { + setFromAxisAngle( axis, angle ) { - constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - this.min = min; - this.max = max; + // assumes axis is normalized - } + const halfAngle = angle / 2, s = Math.sin( halfAngle ); - set( min, max ) { + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); - this.min.copy( min ); - this.max.copy( max ); + this._onChangeCallback(); return this; } - setFromArray( array ) { + setFromRotationMatrix( m ) { - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - for ( let i = 0, l = array.length; i < l; i += 3 ) { + const te = m.elements, - const x = array[ i ]; - const y = array[ i + 1 ]; - const z = array[ i + 2 ]; + 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 ], - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + trace = m11 + m22 + m33; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + if ( trace > 0 ) { - } + const s = 0.5 / Math.sqrt( trace + 1.0 ); - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; - return this; + } else if ( m11 > m22 && m11 > m33 ) { - } + const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - setFromBufferAttribute( attribute ) { + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; - let minX = + Infinity; - let minY = + Infinity; - let minZ = + Infinity; + } else if ( m22 > m33 ) { - let maxX = - Infinity; - let maxY = - Infinity; - let maxZ = - Infinity; + const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - for ( let i = 0, l = attribute.count; i < l; i ++ ) { + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; - const x = attribute.getX( i ); - const y = attribute.getY( i ); - const z = attribute.getZ( i ); + } else { - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; } - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + this._onChangeCallback(); return this; } - setFromPoints( points ) { + setFromUnitVectors( vFrom, vTo ) { - this.makeEmpty(); + // assumes direction vectors vFrom and vTo are normalized - for ( let i = 0, il = points.length; i < il; i ++ ) { + let r = vFrom.dot( vTo ) + 1; - this.expandByPoint( points[ i ] ); + if ( r < Number.EPSILON ) { - } + // vFrom and vTo point in opposite directions - return this; + r = 0; - } + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - setFromCenterAndSize( center, size ) { + this._x = - vFrom.y; + this._y = vFrom.x; + this._z = 0; + this._w = r; - const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); + } else { - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); + this._x = 0; + this._y = - vFrom.z; + this._z = vFrom.y; + this._w = r; - return this; + } - } + } else { - setFromObject( object ) { + // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - this.makeEmpty(); + this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; + this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; + this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; + this._w = r; + + } - return this.expandByObject( object ); + return this.normalize(); } - clone() { + angleTo( q ) { - return new this.constructor().copy( this ); + return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); } - copy( box ) { - - this.min.copy( box.min ); - this.max.copy( box.max ); + rotateTowards( q, step ) { - return this; + const angle = this.angleTo( q ); - } + if ( angle === 0 ) return this; - makeEmpty() { + const t = Math.min( 1, step / angle ); - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; + this.slerp( q, t ); return this; } - isEmpty() { - - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + identity() { - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + return this.set( 0, 0, 0, 1 ); } - getCenter( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); + invert() { - } + // quaternion is assumed to have unit length - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + return this.conjugate(); } - getSize( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); - - } - - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - - } + conjugate() { - expandByPoint( point ) { + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; - this.min.min( point ); - this.max.max( point ); + this._onChangeCallback(); return this; } - expandByVector( vector ) { - - this.min.sub( vector ); - this.max.add( vector ); + dot( v ) { - return this; + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } - expandByScalar( scalar ) { - - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); + lengthSq() { - return this; + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } - expandByObject( object ) { + length() { - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - object.updateWorldMatrix( false, false ); + } - const geometry = object.geometry; + normalize() { - if ( geometry !== undefined ) { + let l = this.length(); - if ( geometry.boundingBox === null ) { + if ( l === 0 ) { - geometry.computeBoundingBox(); + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; - } + } else { - _box$3.copy( geometry.boundingBox ); - _box$3.applyMatrix4( object.matrixWorld ); + l = 1 / l; - this.union( _box$3 ); + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; } - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { - - this.expandByObject( children[ i ] ); - - } + this._onChangeCallback(); return this; } - containsPoint( point ) { + multiply( q, p ) { - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; + if ( p !== undefined ) { - } + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); - containsBox( box ) { + } - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; + return this.multiplyQuaternions( this, q ); } - getParameter( point, target ) { + premultiply( q ) { - // This can potentially have a divide by zero if the box - // has a size dimension of 0. + return this.multiplyQuaternions( q, this ); - if ( target === undefined ) { + } - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); + multiplyQuaternions( a, b ) { - } + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); + const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + const 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; - intersectsBox( box ) { + this._onChangeCallback(); - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; + return this; } - intersectsSphere( sphere ) { - - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$b ); + slerp( qb, t ) { - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); - } + const x = this._x, y = this._y, z = this._z, w = this._w; - intersectsPlane( plane ) { + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. + let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - let min, max; + if ( cosHalfTheta < 0 ) { - if ( plane.normal.x > 0 ) { + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; + cosHalfTheta = - cosHalfTheta; } else { - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; + this.copy( qb ); } - if ( plane.normal.y > 0 ) { - - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; + if ( cosHalfTheta >= 1.0 ) { - } else { + this._w = w; + this._x = x; + this._y = y; + this._z = z; - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; + return this; } - if ( plane.normal.z > 0 ) { + const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; + if ( sqrSinHalfTheta <= Number.EPSILON ) { - } else { + const s = 1 - t; + this._w = s * w + t * this._w; + this._x = s * x + t * this._x; + this._y = s * y + t * this._y; + this._z = s * z + t * this._z; - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; + this.normalize(); + this._onChangeCallback(); + + return this; } - return ( min <= - plane.constant && max >= - plane.constant ); + const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); + const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); + const 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 ); - intersectsTriangle( triangle ) { + this._onChangeCallback(); - if ( this.isEmpty() ) { + return this; - return false; + } - } + slerpQuaternions( qa, qb, t ) { - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); + return this.copy( qa ).slerp( qb, t ); - // translate triangle to aabb origin - _v0$2.subVectors( triangle.a, _center ); - _v1$7.subVectors( triangle.b, _center ); - _v2$3.subVectors( triangle.c, _center ); + } - // compute edge vectors for triangle - _f0.subVectors( _v1$7, _v0$2 ); - _f1.subVectors( _v2$3, _v1$7 ); - _f2.subVectors( _v0$2, _v2$3 ); + random() { - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - let axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { + // Derived from http://planning.cs.uiuc.edu/node198.html + // Note, this source uses w, x, y, z ordering, + // so we swap the order below. - return false; + const u1 = Math.random(); + const sqrt1u1 = Math.sqrt( 1 - u1 ); + const sqrtu1 = Math.sqrt( u1 ); - } + const u2 = 2 * Math.PI * Math.random(); - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { + const u3 = 2 * Math.PI * Math.random(); - return false; + return this.set( + sqrt1u1 * Math.cos( u2 ), + sqrtu1 * Math.sin( u3 ), + sqrtu1 * Math.cos( u3 ), + sqrt1u1 * Math.sin( u2 ), + ); - } + } - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; + equals( quaternion ) { - return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); } - clampPoint( point, target ) { - - if ( target === undefined ) { + fromArray( array, offset = 0 ) { - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; - } + this._onChangeCallback(); - return target.copy( point ).clamp( this.min, this.max ); + return this; } - distanceToPoint( point ) { + toArray( array = [], offset = 0 ) { - const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; - return clampedPoint.sub( point ).length(); + return array; } - getBoundingSphere( target ) { + fromBufferAttribute( attribute, index ) { - if ( target === undefined ) { + this._x = attribute.getX( index ); + this._y = attribute.getY( index ); + this._z = attribute.getZ( index ); + this._w = attribute.getW( index ); - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency + return this; - } + } - this.getCenter( target.center ); + _onChange( callback ) { - target.radius = this.getSize( _vector$b ).length() * 0.5; + this._onChangeCallback = callback; - return target; + return this; } - intersect( box ) { + _onChangeCallback() {} - this.min.max( box.min ); - this.max.min( box.max ); +} - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); +Quaternion.prototype.isQuaternion = true; - return this; +class Vector3 { + + constructor( x = 0, y = 0, z = 0 ) { + + this.x = x; + this.y = y; + this.z = z; } - union( box ) { + set( x, y, z ) { - this.min.min( box.min ); - this.max.max( box.max ); + if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) + + this.x = x; + this.y = y; + this.z = z; return this; } - applyMatrix4( matrix ) { - - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; - - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + setScalar( scalar ) { - this.setFromPoints( _points ); + this.x = scalar; + this.y = scalar; + this.z = scalar; return this; } - translate( offset ) { + setX( x ) { - this.min.add( offset ); - this.max.add( offset ); + this.x = x; return this; } - equals( box ) { + setY( y ) { - return box.min.equals( this.min ) && box.max.equals( this.max ); + this.y = y; - } + return this; -} + } -Box3.prototype.isBox3 = true; + setZ( z ) { -const _points = [ - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3(), - /*@__PURE__*/ new Vector3() -]; + this.z = z; -const _vector$b = /*@__PURE__*/ new Vector3(); + return this; -const _box$3 = /*@__PURE__*/ new Box3(); + } -// triangle centered vertices + setComponent( index, value ) { -const _v0$2 = /*@__PURE__*/ new Vector3(); -const _v1$7 = /*@__PURE__*/ new Vector3(); -const _v2$3 = /*@__PURE__*/ new Vector3(); + switch ( index ) { -// triangle edge vectors + 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 ); -const _f0 = /*@__PURE__*/ new Vector3(); -const _f1 = /*@__PURE__*/ new Vector3(); -const _f2 = /*@__PURE__*/ new Vector3(); + } -const _center = /*@__PURE__*/ new Vector3(); -const _extents = /*@__PURE__*/ new Vector3(); -const _triangleNormal = /*@__PURE__*/ new Vector3(); -const _testAxis = /*@__PURE__*/ new Vector3(); + return this; -function satForAxes( axes, v0, v1, v2, extents ) { + } - for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { + getComponent( index ) { - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - const p0 = v0.dot( _testAxis ); - const p1 = v1.dot( _testAxis ); - const p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { + switch ( index ) { - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; + 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 ); } } - return true; + clone() { -} + return new this.constructor( this.x, this.y, this.z ); -const _box$2 = /*@__PURE__*/ new Box3(); -const _v1$6 = /*@__PURE__*/ new Vector3(); -const _toFarthestPoint = /*@__PURE__*/ new Vector3(); -const _toPoint = /*@__PURE__*/ new Vector3(); + } -class Sphere { + copy( v ) { - constructor( center = new Vector3(), radius = - 1 ) { + this.x = v.x; + this.y = v.y; + this.z = v.z; - this.center = center; - this.radius = radius; + return this; } - set( center, radius ) { + add( v, w ) { - this.center.copy( center ); - this.radius = radius; + if ( w !== undefined ) { + + console.warn( 'THREE.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; } - setFromPoints( points, optionalCenter ) { + addScalar( s ) { - const center = this.center; + this.x += s; + this.y += s; + this.z += s; - if ( optionalCenter !== undefined ) { + return this; - center.copy( optionalCenter ); + } - } else { + addVectors( a, b ) { - _box$2.setFromPoints( points ).getCenter( center ); + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; - } + return this; - let maxRadiusSq = 0; + } - for ( let i = 0, il = points.length; i < il; i ++ ) { + addScaledVector( v, s ) { - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + + return this; + + } + + sub( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); } - this.radius = Math.sqrt( maxRadiusSq ); + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; return this; } - copy( sphere ) { + subScalar( s ) { - this.center.copy( sphere.center ); - this.radius = sphere.radius; + this.x -= s; + this.y -= s; + this.z -= s; return this; } - isEmpty() { + subVectors( a, b ) { - return ( this.radius < 0 ); + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; } - makeEmpty() { + multiply( v, w ) { - this.center.set( 0, 0, 0 ); - this.radius = - 1; + if ( w !== undefined ) { - return this; + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); - } + } - containsPoint( point ) { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + return this; } - distanceToPoint( point ) { + multiplyScalar( scalar ) { - return ( point.distanceTo( this.center ) - this.radius ); + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; } - intersectsSphere( sphere ) { + multiplyVectors( a, b ) { - const radiusSum = this.radius + sphere.radius; + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + return this; } - intersectsBox( box ) { + applyEuler( euler ) { - return box.intersectsSphere( this ); + if ( ! ( euler && euler.isEuler ) ) { - } + console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - intersectsPlane( plane ) { + } - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; + return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); } - clampPoint( point, target ) { + applyAxisAngle( axis, angle ) { - const deltaLengthSq = this.center.distanceToSquared( point ); + return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); - if ( target === undefined ) { + } - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); + applyMatrix3( m ) { - } + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - target.copy( point ); + 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; - if ( deltaLengthSq > ( this.radius * this.radius ) ) { + return this; - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); + } - } + applyNormalMatrix( m ) { - return target; + return this.applyMatrix3( m ).normalize(); } - getBoundingBox( target ) { + applyMatrix4( m ) { - if ( target === undefined ) { + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); + const 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; - if ( this.isEmpty() ) { + return this; - // Empty sphere produces empty bounding box - target.makeEmpty(); - return target; + } - } + applyQuaternion( q ) { - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); + const x = this.x, y = this.y, z = this.z; + const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - return target; + // calculate quat * vector - } + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = - qx * x - qy * y - qz * z; - applyMatrix4( matrix ) { + // calculate result * inverse quat - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); + 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; } - translate( offset ) { - - this.center.add( offset ); + project( camera ) { - return this; + return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); } - expandByPoint( point ) { + unproject( camera ) { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 + return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - _toPoint.subVectors( point, this.center ); + } - const lengthSq = _toPoint.lengthSq(); + transformDirection( m ) { - if ( lengthSq > ( this.radius * this.radius ) ) { + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction - const length = Math.sqrt( lengthSq ); - const missingRadiusHalf = ( length - this.radius ) * 0.5; + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - // Nudge this sphere towards the target point. Add half the missing distance to radius, - // and the other half to position. This gives a tighter enclosure, instead of if - // the whole missing distance were just added to radius. + 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; - this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); - this.radius += missingRadiusHalf; + return this.normalize(); - } + } + + divide( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; return this; } - union( sphere ) { + divideScalar( scalar ) { - // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 + return this.multiplyScalar( 1 / scalar ); - // To enclose another sphere into this sphere, we only need to enclose two points: - // 1) Enclose the farthest point on the other sphere into this sphere. - // 2) Enclose the opposite point of the farthest point into this sphere. + } - _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); + min( v ) { - this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); - this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); + 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; } - equals( sphere ) { + max( v ) { - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + 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; - clone() { + } - return new this.constructor().copy( this ); + clamp( 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 ) ); -const _vector$a = /*@__PURE__*/ new Vector3(); -const _segCenter = /*@__PURE__*/ new Vector3(); -const _segDir = /*@__PURE__*/ new Vector3(); -const _diff = /*@__PURE__*/ new Vector3(); + return this; -const _edge1 = /*@__PURE__*/ new Vector3(); -const _edge2 = /*@__PURE__*/ new Vector3(); -const _normal$1 = /*@__PURE__*/ new Vector3(); + } -class Ray { + clampScalar( minVal, maxVal ) { - constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.origin = origin; - this.direction = direction; + return this; } - set( origin, direction ) { + clampLength( min, max ) { - this.origin.copy( origin ); - this.direction.copy( direction ); + const length = this.length(); - return this; + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); } - copy( ray ) { + floor() { - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); return this; } - at( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); + ceil() { - } + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + return this; } - lookAt( v ) { + round() { - this.direction.copy( v ).sub( this.origin ).normalize(); + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); return this; } - recast( t ) { + roundToZero() { - this.origin.copy( this.at( t, _vector$a ) ); + 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; } - closestPointToPoint( point, target ) { + negate() { - if ( target === undefined ) { + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); + return this; - } + } - target.subVectors( point, this.origin ); + dot( v ) { - const directionDistance = target.dot( this.direction ); + return this.x * v.x + this.y * v.y + this.z * v.z; - if ( directionDistance < 0 ) { + } - return target.copy( this.origin ); + // TODO lengthSquared? - } + lengthSq() { - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + return this.x * this.x + this.y * this.y + this.z * this.z; } - distanceToPoint( point ) { + length() { - return Math.sqrt( this.distanceSqToPoint( point ) ); + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); } - distanceSqToPoint( point ) { + manhattanLength() { - const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - // point behind the ray + } - if ( directionDistance < 0 ) { + normalize() { - return this.origin.distanceToSquared( point ); + return this.divideScalar( this.length() || 1 ); - } + } - _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + setLength( length ) { - return _vector$a.distanceToSquared( point ); + return this.normalize().multiplyScalar( length ); } - distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + lerp( v, alpha ) { - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); + return this; - const segExtent = v0.distanceTo( v1 ) * 0.5; - const a01 = - this.direction.dot( _segDir ); - const b0 = _diff.dot( this.direction ); - const b1 = - _diff.dot( _segDir ); - const c = _diff.lengthSq(); - const det = Math.abs( 1 - a01 * a01 ); - let s0, s1, sqrDist, extDet; + } - if ( det > 0 ) { + lerpVectors( v1, v2, alpha ) { - // The ray and segment are not parallel. + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; + return this; - if ( s0 >= 0 ) { + } - if ( s1 >= - extDet ) { + cross( v, w ) { - if ( s1 <= extDet ) { + if ( w !== undefined ) { - // region 0 - // Minimum at interior points of ray and segment. + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); - const invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + } - } else { + return this.crossVectors( this, v ); - // region 1 + } - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + crossVectors( a, b ) { - } + const ax = a.x, ay = a.y, az = a.z; + const bx = b.x, by = b.y, bz = b.z; - } else { + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; - // region 5 + return this; - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + } - } + projectOnVector( v ) { - } else { + const denominator = v.lengthSq(); - if ( s1 <= - extDet ) { + if ( denominator === 0 ) return this.set( 0, 0, 0 ); - // region 4 + const scalar = v.dot( this ) / denominator; - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + return this.copy( v ).multiplyScalar( scalar ); - } else if ( s1 <= extDet ) { + } - // region 3 + projectOnPlane( planeNormal ) { - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; + _vector$c.copy( this ).projectOnVector( planeNormal ); - } else { + return this.sub( _vector$c ); - // region 2 + } - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + reflect( normal ) { - } + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length - } - - } else { + return this.sub( _vector$c.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - // Ray and segment are parallel. + } - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + angleTo( v ) { - } + const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - if ( optionalPointOnRay ) { + if ( denominator === 0 ) return Math.PI / 2; - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + const theta = this.dot( v ) / denominator; - } + // clamp, to handle numerical problems - if ( optionalPointOnSegment ) { + return Math.acos( clamp( theta, - 1, 1 ) ); - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); + } - } + distanceTo( v ) { - return sqrDist; + return Math.sqrt( this.distanceToSquared( v ) ); } - intersectSphere( sphere, target ) { + distanceToSquared( v ) { - _vector$a.subVectors( sphere.center, this.origin ); - const tca = _vector$a.dot( this.direction ); - const d2 = _vector$a.dot( _vector$a ) - tca * tca; - const radius2 = sphere.radius * sphere.radius; + const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - if ( d2 > radius2 ) return null; + return dx * dx + dy * dy + dz * dz; - const thc = Math.sqrt( radius2 - d2 ); + } - // t0 = first intersect point - entrance on front of sphere - const t0 = tca - thc; + manhattanDistanceTo( v ) { - // t1 = second intersect point - exit point on back of sphere - const t1 = tca + thc; + return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; + } - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); + setFromSpherical( s ) { - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); + return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); } - intersectsSphere( sphere ) { + setFromSphericalCoords( radius, phi, theta ) { - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); + const sinPhiRadius = Math.sin( phi ) * radius; - } + this.x = sinPhiRadius * Math.sin( theta ); + this.y = Math.cos( phi ) * radius; + this.z = sinPhiRadius * Math.cos( theta ); - distanceToPlane( plane ) { + return this; - const denominator = plane.normal.dot( this.direction ); + } - if ( denominator === 0 ) { + setFromCylindrical( c ) { - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { + return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - return 0; + } - } + setFromCylindricalCoords( radius, theta, y ) { - // Null is preferable to undefined since undefined means.... it is undefined + this.x = radius * Math.sin( theta ); + this.y = y; + this.z = radius * Math.cos( theta ); - return null; + return this; - } + } - const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + setFromMatrixPosition( m ) { - // Return if the ray never intersects the plane + const e = m.elements; - return t >= 0 ? t : null; + this.x = e[ 12 ]; + this.y = e[ 13 ]; + this.z = e[ 14 ]; + + return this; } - intersectPlane( plane, target ) { + setFromMatrixScale( m ) { - const t = this.distanceToPlane( plane ); + const sx = this.setFromMatrixColumn( m, 0 ).length(); + const sy = this.setFromMatrixColumn( m, 1 ).length(); + const sz = this.setFromMatrixColumn( m, 2 ).length(); - if ( t === null ) { + this.x = sx; + this.y = sy; + this.z = sz; - return null; + return this; - } + } - return this.at( t, target ); + setFromMatrixColumn( m, index ) { + + return this.fromArray( m.elements, index * 4 ); } - intersectsPlane( plane ) { + setFromMatrix3Column( m, index ) { - // check if the ray lies on the plane first + return this.fromArray( m.elements, index * 3 ); - const distToPoint = plane.distanceToPoint( this.origin ); + } - if ( distToPoint === 0 ) { + setFromEuler( e ) { - return true; + this.x = e._x; + this.y = e._y; + this.z = e._z; - } + return this; - const denominator = plane.normal.dot( this.direction ); + } - if ( denominator * distToPoint < 0 ) { + equals( v ) { - return true; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - } + } - // ray origin is behind the plane (and is pointing behind it) + fromArray( array, offset = 0 ) { - return false; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; - } + return this; - intersectBox( box, target ) { + } - let tmin, tmax, tymin, tymax, tzmin, tzmax; + toArray( array = [], offset = 0 ) { - const invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; - const origin = this.origin; + return array; - if ( invdirx >= 0 ) { + } - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; + fromBufferAttribute( attribute, index, offset ) { - } else { + if ( offset !== undefined ) { - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; + console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); } - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); - } else { + return this; - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; + } - } + random() { - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN + return this; - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + } - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + randomDirection() { - if ( invdirz >= 0 ) { + // Derived from https://mathworld.wolfram.com/SpherePointPicking.html - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; + const u = ( Math.random() - 0.5 ) * 2; + const t = Math.random() * Math.PI * 2; + const f = Math.sqrt( 1 - u ** 2 ); - } else { + this.x = f * Math.cos( t ); + this.y = f * Math.sin( t ); + this.z = u; - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; + return this; - } + } - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + *[ Symbol.iterator ]() { - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + yield this.x; + yield this.y; + yield this.z; - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + } - //return point closest to the ray (positive side) +} - if ( tmax < 0 ) return null; +Vector3.prototype.isVector3 = true; - return this.at( tmin >= 0 ? tmin : tmax, target ); +const _vector$c = /*@__PURE__*/ new Vector3(); +const _quaternion$4 = /*@__PURE__*/ new Quaternion(); - } +class Box3 { - intersectsBox( box ) { + constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { - return this.intersectBox( box, _vector$a ) !== null; + this.min = min; + this.max = max; } - intersectTriangle( a, b, c, backfaceCulling, target ) { + set( min, max ) { - // Compute the offset origin, edges, and normal. + this.min.copy( min ); + this.max.copy( max ); - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + return this; - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal$1.crossVectors( _edge1, _edge2 ); + } - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal$1 ); - let sign; + setFromArray( array ) { - if ( DdN > 0 ) { + let minX = + Infinity; + let minY = + Infinity; + let minZ = + Infinity; - if ( backfaceCulling ) return null; - sign = 1; + let maxX = - Infinity; + let maxY = - Infinity; + let maxZ = - Infinity; - } else if ( DdN < 0 ) { + for ( let i = 0, l = array.length; i < l; i += 3 ) { - sign = - 1; - DdN = - DdN; + const x = array[ i ]; + const y = array[ i + 1 ]; + const z = array[ i + 2 ]; - } else { + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - return null; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; } - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + return this; - return null; + } - } + setFromBufferAttribute( attribute ) { - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); + let minX = + Infinity; + let minY = + Infinity; + let minZ = + Infinity; - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + let maxX = - Infinity; + let maxY = - Infinity; + let maxZ = - Infinity; - return null; + for ( let i = 0, l = attribute.count; i < l; i ++ ) { - } + const x = attribute.getX( i ); + const y = attribute.getY( i ); + const z = attribute.getZ( i ); - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - return null; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; } - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal$1 ); + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); - // t < 0, no intersection - if ( QdN < 0 ) { + return this; - return null; + } + + setFromPoints( points ) { + + this.makeEmpty(); + + for ( let i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ); } - // Ray intersects triangle. - return this.at( QdN / DdN, target ); + return this; } - applyMatrix4( matrix4 ) { + setFromCenterAndSize( center, size ) { - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); + const halfSize = _vector$b.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); return this; } - equals( ray ) { + setFromObject( object, precise = false ) { - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + this.makeEmpty(); + + return this.expandByObject( object, precise ); } @@ -6641,3417 +6558,3274 @@ class Ray { } -} + copy( box ) { -class Matrix4 { + this.min.copy( box.min ); + this.max.copy( box.max ); - constructor() { + return this; - this.elements = [ + } - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + makeEmpty() { - ]; + this.min.x = this.min.y = this.min.z = + Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; - if ( arguments.length > 0 ) { + return this; - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + } - } + isEmpty() { - } + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - const 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; + getCenter( target ) { - return this; + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); } - identity() { + getSize( target ) { - this.set( + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ); + expandByPoint( point ) { + + this.min.min( point ); + this.max.max( point ); return this; } - clone() { + expandByVector( vector ) { - return new Matrix4().fromArray( this.elements ); + this.min.sub( vector ); + this.max.add( vector ); - } + return this; - copy( m ) { + } - const te = this.elements; - const me = m.elements; + expandByScalar( scalar ) { - 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 ]; + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); return this; } - copyPosition( m ) { + expandByObject( object, precise = false ) { - const te = this.elements, me = m.elements; + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; + object.updateWorldMatrix( false, false ); - return this; + const geometry = object.geometry; - } + if ( geometry !== undefined ) { - setFromMatrix3( m ) { + if ( precise && geometry.attributes != undefined && geometry.attributes.position !== undefined ) { - const me = m.elements; + const position = geometry.attributes.position; + for ( let i = 0, l = position.count; i < l; i ++ ) { - this.set( + _vector$b.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); + this.expandByPoint( _vector$b ); - me[ 0 ], me[ 3 ], me[ 6 ], 0, - me[ 1 ], me[ 4 ], me[ 7 ], 0, - me[ 2 ], me[ 5 ], me[ 8 ], 0, - 0, 0, 0, 1 + } - ); + } else { - return this; + if ( geometry.boundingBox === null ) { - } + geometry.computeBoundingBox(); - extractBasis( xAxis, yAxis, zAxis ) { + } - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); + _box$3.copy( geometry.boundingBox ); + _box$3.applyMatrix4( object.matrixWorld ); - return this; + this.union( _box$3 ); - } + } - makeBasis( 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 - ); + const children = object.children; + + for ( let i = 0, l = children.length; i < l; i ++ ) { + + this.expandByObject( children[ i ], precise ); + + } return this; } - extractRotation( m ) { + containsPoint( point ) { - // this method does not support reflection matrices + return point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ? false : true; - const te = this.elements; - const me = m.elements; + } - const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); - const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); - const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); + containsBox( box ) { - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; + return this.min.x <= box.min.x && box.max.x <= this.max.x && + this.min.y <= box.min.y && box.max.y <= this.max.y && + this.min.z <= box.min.z && box.max.z <= this.max.z; - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; + } - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; + getParameter( point, target ) { - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + // This can potentially have a divide by zero if the box + // has a size dimension of 0. - return this; + return target.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); } - makeRotationFromEuler( euler ) { + intersectsBox( box ) { - if ( ! ( euler && euler.isEuler ) ) { + // using 6 splitting planes to rule out intersections. + return box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } - } + intersectsSphere( sphere ) { - const te = this.elements; + // Find the point on the AABB closest to the sphere center. + this.clampPoint( sphere.center, _vector$b ); - const x = euler.x, y = euler.y, z = euler.z; - const a = Math.cos( x ), b = Math.sin( x ); - const c = Math.cos( y ), d = Math.sin( y ); - const e = Math.cos( z ), f = Math.sin( z ); + // If that point is inside the sphere, the AABB and sphere intersect. + return _vector$b.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - if ( euler.order === 'XYZ' ) { + } - const ae = a * e, af = a * f, be = b * e, bf = b * f; + intersectsPlane( plane ) { - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; + let min, max; - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; + if ( plane.normal.x > 0 ) { - } else if ( euler.order === 'YXZ' ) { + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; - const ce = c * e, cf = c * f, de = d * e, df = d * f; + } else { - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; - 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; + if ( plane.normal.y > 0 ) { - } else if ( euler.order === 'ZXY' ) { + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; - const ce = c * e, cf = c * f, de = d * e, df = d * f; + } else { - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; - 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; + if ( plane.normal.z > 0 ) { - } else if ( euler.order === 'ZYX' ) { + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; - const ae = a * e, af = a * f, be = b * e, bf = b * f; + } else { - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; - 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; + return ( min <= - plane.constant && max >= - plane.constant ); - } else if ( euler.order === 'YZX' ) { + } - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; + intersectsTriangle( triangle ) { - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; + if ( this.isEmpty() ) { - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; + return false; - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; + } - } else if ( euler.order === 'XZY' ) { + // compute box center and extents + this.getCenter( _center ); + _extents.subVectors( this.max, _center ); - const ac = a * c, ad = a * d, bc = b * c, bd = b * d; + // translate triangle to aabb origin + _v0$2.subVectors( triangle.a, _center ); + _v1$7.subVectors( triangle.b, _center ); + _v2$3.subVectors( triangle.c, _center ); - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; + // compute edge vectors for triangle + _f0.subVectors( _v1$7, _v0$2 ); + _f1.subVectors( _v2$3, _v1$7 ); + _f2.subVectors( _v0$2, _v2$3 ); - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; + // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb + // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation + // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) + let axes = [ + 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, + _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, + - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 + ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; + return false; } - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; - - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + // test 3 face normals from the aabb + axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ) ) { - return this; + return false; - } + } - makeRotationFromQuaternion( q ) { + // finally testing the face normal of the triangle + // use already existing triangle edge vectors here + _triangleNormal.crossVectors( _f0, _f1 ); + axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - return this.compose( _zero, q, _one ); + return satForAxes( axes, _v0$2, _v1$7, _v2$3, _extents ); } - lookAt( eye, target, up ) { + clampPoint( point, target ) { - const te = this.elements; + return target.copy( point ).clamp( this.min, this.max ); - _z.subVectors( eye, target ); + } - if ( _z.lengthSq() === 0 ) { + distanceToPoint( point ) { - // eye and target are in the same position + const clampedPoint = _vector$b.copy( point ).clamp( this.min, this.max ); - _z.z = 1; + return clampedPoint.sub( point ).length(); - } + } - _z.normalize(); - _x.crossVectors( up, _z ); + getBoundingSphere( target ) { - if ( _x.lengthSq() === 0 ) { + this.getCenter( target.center ); - // up and z are parallel + target.radius = this.getSize( _vector$b ).length() * 0.5; - if ( Math.abs( up.z ) === 1 ) { + return target; - _z.x += 0.0001; + } - } else { + intersect( box ) { - _z.z += 0.0001; + this.min.max( box.min ); + this.max.min( box.max ); - } + // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. + if ( this.isEmpty() ) this.makeEmpty(); - _z.normalize(); - _x.crossVectors( up, _z ); + return this; - } + } - _x.normalize(); - _y.crossVectors( _z, _x ); + union( box ) { - 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; + this.min.min( box.min ); + this.max.max( box.max ); return this; } - multiply( m, n ) { + applyMatrix4( matrix ) { - if ( n !== undefined ) { + // transform of empty box is an empty box. + if ( this.isEmpty() ) return this; - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - } + this.setFromPoints( _points ); - return this.multiplyMatrices( this, m ); + return this; } - premultiply( m ) { + translate( offset ) { - return this.multiplyMatrices( m, this ); + this.min.add( offset ); + this.max.add( offset ); + + return this; } - multiplyMatrices( a, b ) { + equals( box ) { - const ae = a.elements; - const be = b.elements; - const te = this.elements; + return box.min.equals( this.min ) && box.max.equals( this.max ); - const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + } - const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - const 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; +Box3.prototype.isBox3 = true; - 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; +const _points = [ + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3() +]; - 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; +const _vector$b = /*@__PURE__*/ new Vector3(); - 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; +const _box$3 = /*@__PURE__*/ new Box3(); - return this; +// triangle centered vertices - } +const _v0$2 = /*@__PURE__*/ new Vector3(); +const _v1$7 = /*@__PURE__*/ new Vector3(); +const _v2$3 = /*@__PURE__*/ new Vector3(); - multiplyScalar( s ) { +// triangle edge vectors - const te = this.elements; +const _f0 = /*@__PURE__*/ new Vector3(); +const _f1 = /*@__PURE__*/ new Vector3(); +const _f2 = /*@__PURE__*/ new Vector3(); - 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; +const _center = /*@__PURE__*/ new Vector3(); +const _extents = /*@__PURE__*/ new Vector3(); +const _triangleNormal = /*@__PURE__*/ new Vector3(); +const _testAxis = /*@__PURE__*/ new Vector3(); - return this; +function satForAxes( axes, v0, v1, v2, extents ) { - } + for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - determinant() { + _testAxis.fromArray( axes, i ); + // project the aabb onto the separating axis + const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); + // project all 3 vertices of the triangle onto the separating axis + const p0 = v0.dot( _testAxis ); + const p1 = v1.dot( _testAxis ); + const p2 = v2.dot( _testAxis ); + // actual test, basically see if either of the most extreme of the triangle points intersects r + if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - const te = this.elements; + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is separating and we can exit + return false; - const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - const 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 - ) + return true; - ); +} - } +const _box$2 = /*@__PURE__*/ new Box3(); +const _v1$6 = /*@__PURE__*/ new Vector3(); +const _toFarthestPoint = /*@__PURE__*/ new Vector3(); +const _toPoint = /*@__PURE__*/ new Vector3(); - transpose() { +class Sphere { - const te = this.elements; - let tmp; + constructor( center = new Vector3(), radius = - 1 ) { - 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; + this.center = center; + this.radius = radius; - 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; + } + + set( center, radius ) { + + this.center.copy( center ); + this.radius = radius; return this; } - setPosition( x, y, z ) { + setFromPoints( points, optionalCenter ) { - const te = this.elements; + const center = this.center; - if ( x.isVector3 ) { + if ( optionalCenter !== undefined ) { - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; + center.copy( optionalCenter ); } else { - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; + _box$2.setFromPoints( points ).getCenter( center ); } - return this; + let maxRadiusSq = 0; - } + for ( let i = 0, il = points.length; i < il; i ++ ) { - invert() { + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - const te = this.elements, + } - n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], - n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], - n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], - n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], + this.radius = Math.sqrt( maxRadiusSq ); - 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; + return this; - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + } - if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + copy( sphere ) { - const detInv = 1 / det; + this.center.copy( sphere.center ); + this.radius = sphere.radius; - 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; + return this; - 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; + isEmpty() { - 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.radius < 0 ); + + } + + makeEmpty() { + + this.center.set( 0, 0, 0 ); + this.radius = - 1; return this; } - scale( v ) { + containsPoint( point ) { - const te = this.elements; - const x = v.x, y = v.y, z = v.z; + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - 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; + distanceToPoint( point ) { - } + return ( point.distanceTo( this.center ) - this.radius ); - getMaxScaleOnAxis() { + } - const te = this.elements; + intersectsSphere( sphere ) { - const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + const radiusSum = this.radius + sphere.radius; - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); } - makeTranslation( x, y, z ) { + intersectsBox( box ) { - this.set( + return box.intersectsSphere( this ); - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 + } - ); + intersectsPlane( plane ) { - return this; + return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; } - makeRotationX( theta ) { + clampPoint( point, target ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + const deltaLengthSq = this.center.distanceToSquared( point ); - this.set( + target.copy( point ); - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 + if ( deltaLengthSq > ( this.radius * this.radius ) ) { - ); + target.sub( this.center ).normalize(); + target.multiplyScalar( this.radius ).add( this.center ); - return this; + } + + return target; } - makeRotationY( theta ) { + getBoundingBox( target ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + if ( this.isEmpty() ) { - this.set( + // Empty sphere produces empty bounding box + target.makeEmpty(); + return target; - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 + } - ); + target.set( this.center, this.center ); + target.expandByScalar( this.radius ); - return this; + return target; } - makeRotationZ( theta ) { + applyMatrix4( matrix ) { - const c = Math.cos( theta ), s = Math.sin( theta ); + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); - this.set( + return this; - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ); + translate( offset ) { + + this.center.add( offset ); return this; } - makeRotationAxis( axis, angle ) { + expandByPoint( point ) { - // Based on http://www.gamedev.net/reference/articles/article1199.asp + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L649-L671 - const c = Math.cos( angle ); - const s = Math.sin( angle ); - const t = 1 - c; - const x = axis.x, y = axis.y, z = axis.z; - const 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 - - ); + _toPoint.subVectors( point, this.center ); - return this; + const lengthSq = _toPoint.lengthSq(); - } + if ( lengthSq > ( this.radius * this.radius ) ) { - makeScale( x, y, z ) { + const length = Math.sqrt( lengthSq ); + const missingRadiusHalf = ( length - this.radius ) * 0.5; - this.set( + // Nudge this sphere towards the target point. Add half the missing distance to radius, + // and the other half to position. This gives a tighter enclosure, instead of if + // the whole missing distance were just added to radius. - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 + this.center.add( _toPoint.multiplyScalar( missingRadiusHalf / length ) ); + this.radius += missingRadiusHalf; - ); + } return this; } - makeShear( x, y, z ) { - - this.set( + union( sphere ) { - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 + // from https://github.com/juj/MathGeoLib/blob/2940b99b99cfe575dd45103ef20f4019dee15b54/src/Geometry/Sphere.cpp#L759-L769 - ); + // To enclose another sphere into this sphere, we only need to enclose two points: + // 1) Enclose the farthest point on the other sphere into this sphere. + // 2) Enclose the opposite point of the farthest point into this sphere. - return this; + if ( this.center.equals( sphere.center ) === true ) { - } + _toFarthestPoint.set( 0, 0, 1 ).multiplyScalar( sphere.radius ); - compose( position, quaternion, scale ) { - const te = this.elements; + } else { - const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - const x2 = x + x, y2 = y + y, z2 = z + z; - const xx = x * x2, xy = x * y2, xz = x * z2; - const yy = y * y2, yz = y * z2, zz = z * z2; - const wx = w * x2, wy = w * y2, wz = w * z2; + _toFarthestPoint.subVectors( sphere.center, this.center ).normalize().multiplyScalar( sphere.radius ); - const sx = scale.x, sy = scale.y, sz = scale.z; + } - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; + this.expandByPoint( _v1$6.copy( sphere.center ).add( _toFarthestPoint ) ); + this.expandByPoint( _v1$6.copy( sphere.center ).sub( _toFarthestPoint ) ); - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; + return this; - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; + } - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; + equals( sphere ) { - return this; + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } - decompose( position, quaternion, scale ) { + clone() { - const te = this.elements; + return new this.constructor().copy( this ); - let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + } - // if determine is negative, we need to invert one scale - const det = this.determinant(); - if ( det < 0 ) sx = - sx; +} - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; +const _vector$a = /*@__PURE__*/ new Vector3(); +const _segCenter = /*@__PURE__*/ new Vector3(); +const _segDir = /*@__PURE__*/ new Vector3(); +const _diff = /*@__PURE__*/ new Vector3(); - // scale the rotation part - _m1$2.copy( this ); +const _edge1 = /*@__PURE__*/ new Vector3(); +const _edge2 = /*@__PURE__*/ new Vector3(); +const _normal$1 = /*@__PURE__*/ new Vector3(); - const invSX = 1 / sx; - const invSY = 1 / sy; - const invSZ = 1 / sz; +class Ray { - _m1$2.elements[ 0 ] *= invSX; - _m1$2.elements[ 1 ] *= invSX; - _m1$2.elements[ 2 ] *= invSX; + constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { - _m1$2.elements[ 4 ] *= invSY; - _m1$2.elements[ 5 ] *= invSY; - _m1$2.elements[ 6 ] *= invSY; + this.origin = origin; + this.direction = direction; - _m1$2.elements[ 8 ] *= invSZ; - _m1$2.elements[ 9 ] *= invSZ; - _m1$2.elements[ 10 ] *= invSZ; + } - quaternion.setFromRotationMatrix( _m1$2 ); + set( origin, direction ) { - scale.x = sx; - scale.y = sy; - scale.z = sz; + this.origin.copy( origin ); + this.direction.copy( direction ); return this; } - makePerspective( left, right, top, bottom, near, far ) { + copy( ray ) { - if ( far === undefined ) { + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); + return this; - } + } - const te = this.elements; - const x = 2 * near / ( right - left ); - const y = 2 * near / ( top - bottom ); + at( t, target ) { - const a = ( right + left ) / ( right - left ); - const b = ( top + bottom ) / ( top - bottom ); - const c = - ( far + near ) / ( far - near ); - const d = - 2 * far * near / ( far - near ); + return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - 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; + lookAt( v ) { - } + this.direction.copy( v ).sub( this.origin ).normalize(); - makeOrthographic( left, right, top, bottom, near, far ) { + return this; - const te = this.elements; - const w = 1.0 / ( right - left ); - const h = 1.0 / ( top - bottom ); - const p = 1.0 / ( far - near ); + } - const x = ( right + left ) * w; - const y = ( top + bottom ) * h; - const z = ( far + near ) * p; + recast( t ) { - 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; + this.origin.copy( this.at( t, _vector$a ) ); return this; } - equals( matrix ) { + closestPointToPoint( point, target ) { - const te = this.elements; - const me = matrix.elements; + target.subVectors( point, this.origin ); - for ( let i = 0; i < 16; i ++ ) { + const directionDistance = target.dot( this.direction ); - if ( te[ i ] !== me[ i ] ) return false; + if ( directionDistance < 0 ) { + + return target.copy( this.origin ); } - return true; + return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); } - fromArray( array, offset = 0 ) { - - for ( let i = 0; i < 16; i ++ ) { - - this.elements[ i ] = array[ i + offset ]; - - } + distanceToPoint( point ) { - return this; + return Math.sqrt( this.distanceSqToPoint( point ) ); } - toArray( array = [], offset = 0 ) { + distanceSqToPoint( point ) { - const te = this.elements; + const directionDistance = _vector$a.subVectors( point, this.origin ).dot( this.direction ); - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; + // point behind the ray - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; + if ( directionDistance < 0 ) { - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; + return this.origin.distanceToSquared( point ); - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; + } - return array; + _vector$a.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - } + return _vector$a.distanceToSquared( point ); -} + } -Matrix4.prototype.isMatrix4 = true; + distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { -const _v1$5 = /*@__PURE__*/ new Vector3(); -const _m1$2 = /*@__PURE__*/ new Matrix4(); -const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); -const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); -const _x = /*@__PURE__*/ new Vector3(); -const _y = /*@__PURE__*/ new Vector3(); -const _z = /*@__PURE__*/ new Vector3(); + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment -const _matrix$1 = /*@__PURE__*/ new Matrix4(); -const _quaternion$3 = /*@__PURE__*/ new Quaternion(); + _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + _segDir.copy( v1 ).sub( v0 ).normalize(); + _diff.copy( this.origin ).sub( _segCenter ); -class Euler { + const segExtent = v0.distanceTo( v1 ) * 0.5; + const a01 = - this.direction.dot( _segDir ); + const b0 = _diff.dot( this.direction ); + const b1 = - _diff.dot( _segDir ); + const c = _diff.lengthSq(); + const det = Math.abs( 1 - a01 * a01 ); + let s0, s1, sqrDist, extDet; - constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { + if ( det > 0 ) { - this._x = x; - this._y = y; - this._z = z; - this._order = order; + // The ray and segment are not parallel. - } + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; - get x() { + if ( s0 >= 0 ) { - return this._x; + if ( s1 >= - extDet ) { - } + if ( s1 <= extDet ) { - set x( value ) { + // region 0 + // Minimum at interior points of ray and segment. - this._x = value; - this._onChangeCallback(); + const invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - } + } else { - get y() { + // region 1 - return this._y; + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - set y( value ) { + } else { - this._y = value; - this._onChangeCallback(); + // region 5 - } + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - get z() { + } - return this._z; + } else { - } + if ( s1 <= - extDet ) { - set z( value ) { + // region 4 - this._z = value; - this._onChangeCallback(); + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } else if ( s1 <= extDet ) { - get order() { + // region 3 - return this._order; + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; - } + } else { - set order( value ) { + // region 2 - this._order = value; - this._onChangeCallback(); + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - set( x, y, z, order ) { + } - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; + } else { - this._onChangeCallback(); + // Ray and segment are parallel. - return this; + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - clone() { + if ( optionalPointOnRay ) { - return new this.constructor( this._x, this._y, this._z, this._order ); + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - } + } - copy( euler ) { + if ( optionalPointOnSegment ) { - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; + optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); - this._onChangeCallback(); + } - return this; + return sqrDist; } - setFromRotationMatrix( m, order, update ) { - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + intersectSphere( sphere, target ) { - const te = m.elements; - const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + _vector$a.subVectors( sphere.center, this.origin ); + const tca = _vector$a.dot( this.direction ); + const d2 = _vector$a.dot( _vector$a ) - tca * tca; + const radius2 = sphere.radius * sphere.radius; - order = order || this._order; + if ( d2 > radius2 ) return null; - switch ( order ) { + const thc = Math.sqrt( radius2 - d2 ); - case 'XYZ': + // t0 = first intersect point - entrance on front of sphere + const t0 = tca - thc; - this._y = Math.asin( clamp( m13, - 1, 1 ) ); + // t1 = second intersect point - exit point on back of sphere + const t1 = tca + thc; - if ( Math.abs( m13 ) < 0.9999999 ) { + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, target ); - } else { + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, target ); - this._x = Math.atan2( m32, m22 ); - this._z = 0; + } - } + intersectsSphere( sphere ) { - break; + return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - case 'YXZ': + } - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + distanceToPlane( plane ) { - if ( Math.abs( m23 ) < 0.9999999 ) { + const denominator = plane.normal.dot( this.direction ); - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); + if ( denominator === 0 ) { - } else { + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { - this._y = Math.atan2( - m31, m11 ); - this._z = 0; + return 0; - } + } - break; + // Null is preferable to undefined since undefined means.... it is undefined - case 'ZXY': + return null; - this._x = Math.asin( clamp( m32, - 1, 1 ) ); + } - if ( Math.abs( m32 ) < 0.9999999 ) { + const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); + // Return if the ray never intersects the plane - } else { + return t >= 0 ? t : null; - this._y = 0; - this._z = Math.atan2( m21, m11 ); + } - } + intersectPlane( plane, target ) { - break; + const t = this.distanceToPlane( plane ); - case 'ZYX': + if ( t === null ) { - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + return null; - if ( Math.abs( m31 ) < 0.9999999 ) { + } - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); + return this.at( t, target ); - } else { + } - this._x = 0; - this._z = Math.atan2( - m12, m22 ); + intersectsPlane( plane ) { - } + // check if the ray lies on the plane first - break; + const distToPoint = plane.distanceToPoint( this.origin ); - case 'YZX': + if ( distToPoint === 0 ) { - this._z = Math.asin( clamp( m21, - 1, 1 ) ); + return true; - if ( Math.abs( m21 ) < 0.9999999 ) { + } - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); + const denominator = plane.normal.dot( this.direction ); - } else { + if ( denominator * distToPoint < 0 ) { - this._x = 0; - this._y = Math.atan2( m13, m33 ); + return true; - } + } - break; + // ray origin is behind the plane (and is pointing behind it) - case 'XZY': + return false; - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + } - if ( Math.abs( m12 ) < 0.9999999 ) { + intersectBox( box, target ) { - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); + let tmin, tmax, tymin, tymax, tzmin, tzmax; - } else { + const invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; - this._x = Math.atan2( - m23, m33 ); - this._y = 0; + const origin = this.origin; - } + if ( invdirx >= 0 ) { - break; + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; - default: + } else { - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; } - this._order = order; - - if ( update !== false ) this._onChangeCallback(); + if ( invdiry >= 0 ) { - return this; + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; - } + } else { - setFromQuaternion( q, order, update ) { + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; - _matrix$1.makeRotationFromQuaternion( q ); + } - return this.setFromRotationMatrix( _matrix$1, order, update ); + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - } + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN - setFromVector3( v, order ) { + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - return this.set( v.x, v.y, v.z, order || this._order ); + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - } + if ( invdirz >= 0 ) { - reorder( newOrder ) { - - // WARNING: this discards revolution information -bhouston - - _quaternion$3.setFromEuler( this ); + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; - return this.setFromQuaternion( _quaternion$3, newOrder ); + } else { - } + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; - equals( euler ) { + } - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - } + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - fromArray( array ) { + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + //return point closest to the ray (positive side) - this._onChangeCallback(); + if ( tmax < 0 ) return null; - return this; + return this.at( tmin >= 0 ? tmin : tmax, target ); } - toArray( array = [], offset = 0 ) { - - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; + intersectsBox( box ) { - return array; + return this.intersectBox( box, _vector$a ) !== null; } - toVector3( optionalResult ) { - - if ( optionalResult ) { - - return optionalResult.set( this._x, this._y, this._z ); + intersectTriangle( a, b, c, backfaceCulling, target ) { - } else { + // Compute the offset origin, edges, and normal. - return new Vector3( this._x, this._y, this._z ); + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - } + _edge1.subVectors( b, a ); + _edge2.subVectors( c, a ); + _normal$1.crossVectors( _edge1, _edge2 ); - } + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + let DdN = this.direction.dot( _normal$1 ); + let sign; - _onChange( callback ) { + if ( DdN > 0 ) { - this._onChangeCallback = callback; + if ( backfaceCulling ) return null; + sign = 1; - return this; + } else if ( DdN < 0 ) { - } + sign = - 1; + DdN = - DdN; - _onChangeCallback() {} + } else { -} + return null; -Euler.prototype.isEuler = true; + } -Euler.DefaultOrder = 'XYZ'; -Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + _diff.subVectors( this.origin, a ); + const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); -class Layers { + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { - constructor() { + return null; - this.mask = 1 | 0; + } - } + const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - set( channel ) { + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { - this.mask = 1 << channel | 0; + return null; - } + } - enable( channel ) { + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { - this.mask |= 1 << channel | 0; + return null; - } + } - enableAll() { + // Line intersects triangle, check if ray does. + const QdN = - sign * _diff.dot( _normal$1 ); - this.mask = 0xffffffff | 0; + // t < 0, no intersection + if ( QdN < 0 ) { - } + return null; - toggle( channel ) { + } - this.mask ^= 1 << channel | 0; + // Ray intersects triangle. + return this.at( QdN / DdN, target ); } - disable( channel ) { + applyMatrix4( matrix4 ) { - this.mask &= ~ ( 1 << channel | 0 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.transformDirection( matrix4 ); + + return this; } - disableAll() { + equals( ray ) { - this.mask = 0; + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } - test( layers ) { + clone() { - return ( this.mask & layers.mask ) !== 0; + return new this.constructor().copy( this ); } } -let _object3DId = 0; - -const _v1$4 = new Vector3(); -const _q1 = new Quaternion(); -const _m1$1 = new Matrix4(); -const _target = new Vector3(); - -const _position$3 = new Vector3(); -const _scale$2 = new Vector3(); -const _quaternion$2 = new Quaternion(); - -const _xAxis = new Vector3( 1, 0, 0 ); -const _yAxis = new Vector3( 0, 1, 0 ); -const _zAxis = new Vector3( 0, 0, 1 ); - -const _addedEvent = { type: 'added' }; -const _removedEvent = { type: 'removed' }; - -class Object3D extends EventDispatcher$1 { +class Matrix4 { constructor() { - super(); - - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - - this.uuid = generateUUID(); + this.elements = [ - this.name = ''; - this.type = 'Object3D'; + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - this.parent = null; - this.children = []; + ]; - this.up = Object3D.DefaultUp.clone(); + if ( arguments.length > 0 ) { - const position = new Vector3(); - const rotation = new Euler(); - const quaternion = new Quaternion(); - const scale = new Vector3( 1, 1, 1 ); + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - function onRotationChange() { + } - quaternion.setFromEuler( rotation, false ); + } - } + set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - function onQuaternionChange() { + const te = this.elements; - rotation.setFromQuaternion( quaternion, undefined, false ); + 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; - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); + } - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); + identity() { - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); + this.set( - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - this.layers = new Layers(); - this.visible = true; + ); - this.castShadow = false; - this.receiveShadow = false; + return this; - this.frustumCulled = true; - this.renderOrder = 0; + } - this.animations = []; + clone() { - this.userData = {}; + return new Matrix4().fromArray( this.elements ); } - onBeforeRender() {} - onAfterRender() {} - - applyMatrix4( matrix ) { + copy( m ) { - if ( this.matrixAutoUpdate ) this.updateMatrix(); + const te = this.elements; + const me = m.elements; - this.matrix.premultiply( matrix ); + 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 ]; - this.matrix.decompose( this.position, this.quaternion, this.scale ); + return this; } - applyQuaternion( q ) { + copyPosition( m ) { - this.quaternion.premultiply( q ); + const te = this.elements, me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; return this; } - setRotationFromAxisAngle( axis, angle ) { + setFromMatrix3( m ) { - // assumes axis is normalized + const me = m.elements; - this.quaternion.setFromAxisAngle( axis, angle ); + this.set( - } + me[ 0 ], me[ 3 ], me[ 6 ], 0, + me[ 1 ], me[ 4 ], me[ 7 ], 0, + me[ 2 ], me[ 5 ], me[ 8 ], 0, + 0, 0, 0, 1 - setRotationFromEuler( euler ) { + ); - this.quaternion.setFromEuler( euler, true ); + return this; } - setRotationFromMatrix( m ) { + extractBasis( xAxis, yAxis, zAxis ) { - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + xAxis.setFromMatrixColumn( this, 0 ); + yAxis.setFromMatrixColumn( this, 1 ); + zAxis.setFromMatrixColumn( this, 2 ); - this.quaternion.setFromRotationMatrix( m ); + return this; } - setRotationFromQuaternion( q ) { + makeBasis( xAxis, yAxis, zAxis ) { - // assumes q is normalized + 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 + ); - this.quaternion.copy( q ); + return this; } - rotateOnAxis( axis, angle ) { - - // rotate object on axis in object space - // axis is assumed to be normalized - - _q1.setFromAxisAngle( axis, angle ); + extractRotation( m ) { - this.quaternion.multiply( _q1 ); + // this method does not support reflection matrices - return this; + const te = this.elements; + const me = m.elements; - } + const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); + const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); + const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); - rotateOnWorldAxis( axis, angle ) { + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + te[ 3 ] = 0; - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + te[ 7 ] = 0; - _q1.setFromAxisAngle( axis, angle ); + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + te[ 11 ] = 0; - this.quaternion.premultiply( _q1 ); + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; return this; } - rotateX( angle ) { + makeRotationFromEuler( euler ) { - return this.rotateOnAxis( _xAxis, angle ); + if ( ! ( euler && euler.isEuler ) ) { - } + console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - rotateY( angle ) { + } - return this.rotateOnAxis( _yAxis, angle ); + const te = this.elements; - } + const x = euler.x, y = euler.y, z = euler.z; + const a = Math.cos( x ), b = Math.sin( x ); + const c = Math.cos( y ), d = Math.sin( y ); + const e = Math.cos( z ), f = Math.sin( z ); - rotateZ( angle ) { + if ( euler.order === 'XYZ' ) { - return this.rotateOnAxis( _zAxis, angle ); + const ae = a * e, af = a * f, be = b * e, bf = b * f; - } + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; - translateOnAxis( axis, distance ) { + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; - // translate object by distance along axis in object space - // axis is assumed to be normalized + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; - _v1$4.copy( axis ).applyQuaternion( this.quaternion ); + } else if ( euler.order === 'YXZ' ) { - this.position.add( _v1$4.multiplyScalar( distance ) ); + const ce = c * e, cf = c * f, de = d * e, df = d * f; - return this; + 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; - translateX( distance ) { + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; - return this.translateOnAxis( _xAxis, distance ); + } else if ( euler.order === 'ZXY' ) { - } + const ce = c * e, cf = c * f, de = d * e, df = d * f; - translateY( distance ) { + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; - return this.translateOnAxis( _yAxis, distance ); + 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; - translateZ( distance ) { + } else if ( euler.order === 'ZYX' ) { - return this.translateOnAxis( _zAxis, distance ); + const 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; - localToWorld( vector ) { + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; - return vector.applyMatrix4( this.matrixWorld ); + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; - } + } else if ( euler.order === 'YZX' ) { - worldToLocal( vector ) { + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); + 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; - lookAt( x, y, z ) { + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; - // This method does not support objects having non-uniformly-scaled parent(s) + } else if ( euler.order === 'XZY' ) { - if ( x.isVector3 ) { + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - _target.copy( x ); + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; - } else { + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; - _target.set( x, y, z ); + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; } - const parent = this.parent; + // bottom row + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; - this.updateWorldMatrix( true, false ); + // last column + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - _position$3.setFromMatrixPosition( this.matrixWorld ); + return this; - if ( this.isCamera || this.isLight ) { + } - _m1$1.lookAt( _position$3, _target, this.up ); + makeRotationFromQuaternion( q ) { - } else { + return this.compose( _zero, q, _one ); - _m1$1.lookAt( _target, _position$3, this.up ); + } - } + lookAt( eye, target, up ) { - this.quaternion.setFromRotationMatrix( _m1$1 ); + const te = this.elements; - if ( parent ) { + _z.subVectors( eye, target ); - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.invert() ); + if ( _z.lengthSq() === 0 ) { - } + // eye and target are in the same position - } + _z.z = 1; - add( object ) { + } - if ( arguments.length > 1 ) { + _z.normalize(); + _x.crossVectors( up, _z ); - for ( let i = 0; i < arguments.length; i ++ ) { + if ( _x.lengthSq() === 0 ) { - this.add( arguments[ i ] ); + // up and z are parallel - } + if ( Math.abs( up.z ) === 1 ) { - return this; + _z.x += 0.0001; - } + } else { - if ( object === this ) { + _z.z += 0.0001; - console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); - return this; + } - } + _z.normalize(); + _x.crossVectors( up, _z ); - if ( object && object.isObject3D ) { + } - if ( object.parent !== null ) { + _x.normalize(); + _y.crossVectors( _z, _x ); - object.parent.remove( object ); + 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; - object.parent = this; - this.children.push( object ); + } - object.dispatchEvent( _addedEvent ); + multiply( m, n ) { - } else { + if ( n !== undefined ) { - console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); } - return this; + return this.multiplyMatrices( this, m ); } - remove( object ) { - - if ( arguments.length > 1 ) { + premultiply( m ) { - for ( let i = 0; i < arguments.length; i ++ ) { + return this.multiplyMatrices( m, this ); - this.remove( arguments[ i ] ); + } - } + multiplyMatrices( a, b ) { - return this; + const ae = a.elements; + const be = b.elements; + const te = this.elements; - } + const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - const index = this.children.indexOf( object ); + const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - if ( index !== - 1 ) { + 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; - object.parent = null; - this.children.splice( index, 1 ); + 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; - object.dispatchEvent( _removedEvent ); + 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; } - clear() { - - for ( let i = 0; i < this.children.length; i ++ ) { - - const object = this.children[ i ]; - - object.parent = null; - - object.dispatchEvent( _removedEvent ); + multiplyScalar( s ) { - } + const te = this.elements; - this.children.length = 0; + 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; - } - attach( object ) { + determinant() { - // adds object as a child of this, while maintaining the object's world transform + const te = this.elements; - this.updateWorldMatrix( true, false ); + const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - _m1$1.copy( this.matrixWorld ).invert(); + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - if ( object.parent !== null ) { + 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 + ) - object.parent.updateWorldMatrix( true, false ); + ); - _m1$1.multiply( object.parent.matrixWorld ); + } - } + transpose() { - object.applyMatrix4( _m1$1 ); + const te = this.elements; + let tmp; - this.add( object ); + 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; - object.updateWorldMatrix( false, true ); + 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; } - getObjectById( id ) { - - return this.getObjectByProperty( 'id', id ); + setPosition( x, y, z ) { - } + const te = this.elements; - getObjectByName( name ) { + if ( x.isVector3 ) { - return this.getObjectByProperty( 'name', name ); + te[ 12 ] = x.x; + te[ 13 ] = x.y; + te[ 14 ] = x.z; - } + } else { - getObjectByProperty( name, value ) { + te[ 12 ] = x; + te[ 13 ] = y; + te[ 14 ] = z; - if ( this[ name ] === value ) return this; + } - for ( let i = 0, l = this.children.length; i < l; i ++ ) { + return this; - const child = this.children[ i ]; - const object = child.getObjectByProperty( name, value ); + } - if ( object !== undefined ) { + invert() { - return object; + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + const te = this.elements, - } + n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], + n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], + n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], + n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 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; - return undefined; + const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - } + if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - getWorldPosition( target ) { + const detInv = 1 / det; - if ( target === undefined ) { + 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; - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); + 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; - this.updateWorldMatrix( true, false ); + 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 target.setFromMatrixPosition( this.matrixWorld ); + return this; } - getWorldQuaternion( target ) { + scale( v ) { - if ( target === undefined ) { + const te = this.elements; + const x = v.x, y = v.y, z = v.z; - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); + 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; - this.updateWorldMatrix( true, false ); + } - this.matrixWorld.decompose( _position$3, target, _scale$2 ); + getMaxScaleOnAxis() { - return target; + const te = this.elements; - } + const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - getWorldScale( target ) { + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - if ( target === undefined ) { + } - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); + makeTranslation( x, y, z ) { - } + this.set( - this.updateWorldMatrix( true, false ); + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 - this.matrixWorld.decompose( _position$3, _quaternion$2, target ); + ); - return target; + return this; } - getWorldDirection( target ) { - - if ( target === undefined ) { + makeRotationX( theta ) { - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); + const c = Math.cos( theta ), s = Math.sin( theta ); - } + this.set( - this.updateWorldMatrix( true, false ); + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 - const e = this.matrixWorld.elements; + ); - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); + return this; } - raycast() {} - - traverse( callback ) { + makeRotationY( theta ) { - callback( this ); + const c = Math.cos( theta ), s = Math.sin( theta ); - const children = this.children; + this.set( - for ( let i = 0, l = children.length; i < l; i ++ ) { + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 - children[ i ].traverse( callback ); + ); - } + return this; } - traverseVisible( callback ) { - - if ( this.visible === false ) return; + makeRotationZ( theta ) { - callback( this ); + const c = Math.cos( theta ), s = Math.sin( theta ); - const children = this.children; + this.set( - for ( let i = 0, l = children.length; i < l; i ++ ) { + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - children[ i ].traverseVisible( callback ); + ); - } + return this; } - traverseAncestors( callback ) { + makeRotationAxis( axis, angle ) { - const parent = this.parent; + // Based on http://www.gamedev.net/reference/articles/article1199.asp - if ( parent !== null ) { + const c = Math.cos( angle ); + const s = Math.sin( angle ); + const t = 1 - c; + const x = axis.x, y = axis.y, z = axis.z; + const tx = t * x, ty = t * y; - callback( parent ); + this.set( - parent.traverseAncestors( callback ); + 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; } - updateMatrix() { + makeScale( x, y, z ) { - this.matrix.compose( this.position, this.quaternion, this.scale ); + this.set( - this.matrixWorldNeedsUpdate = true; + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 - } + ); - updateMatrixWorld( force ) { + return this; - if ( this.matrixAutoUpdate ) this.updateMatrix(); + } - if ( this.matrixWorldNeedsUpdate || force ) { + makeShear( xy, xz, yx, yz, zx, zy ) { - if ( this.parent === null ) { + this.set( - this.matrixWorld.copy( this.matrix ); + 1, yx, zx, 0, + xy, 1, zy, 0, + xz, yz, 1, 0, + 0, 0, 0, 1 - } else { + ); - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + return this; - } + } - this.matrixWorldNeedsUpdate = false; + compose( position, quaternion, scale ) { - force = true; + const te = this.elements; - } + const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; + const x2 = x + x, y2 = y + y, z2 = z + z; + const xx = x * x2, xy = x * y2, xz = x * z2; + const yy = y * y2, yz = y * z2, zz = z * z2; + const wx = w * x2, wy = w * y2, wz = w * z2; - // update children + const sx = scale.x, sy = scale.y, sz = scale.z; - const children = this.children; + te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; + te[ 1 ] = ( xy + wz ) * sx; + te[ 2 ] = ( xz - wy ) * sx; + te[ 3 ] = 0; - for ( let i = 0, l = children.length; i < l; i ++ ) { + te[ 4 ] = ( xy - wz ) * sy; + te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; + te[ 6 ] = ( yz + wx ) * sy; + te[ 7 ] = 0; - children[ i ].updateMatrixWorld( force ); + te[ 8 ] = ( xz + wy ) * sz; + te[ 9 ] = ( yz - wx ) * sz; + te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; + te[ 11 ] = 0; - } + te[ 12 ] = position.x; + te[ 13 ] = position.y; + te[ 14 ] = position.z; + te[ 15 ] = 1; + + return this; } - updateWorldMatrix( updateParents, updateChildren ) { + decompose( position, quaternion, scale ) { - const parent = this.parent; + const te = this.elements; - if ( updateParents === true && parent !== null ) { + let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - parent.updateWorldMatrix( true, false ); + // if determine is negative, we need to invert one scale + const det = this.determinant(); + if ( det < 0 ) sx = - sx; - } + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; - if ( this.matrixAutoUpdate ) this.updateMatrix(); + // scale the rotation part + _m1$2.copy( this ); - if ( this.parent === null ) { + const invSX = 1 / sx; + const invSY = 1 / sy; + const invSZ = 1 / sz; - this.matrixWorld.copy( this.matrix ); + _m1$2.elements[ 0 ] *= invSX; + _m1$2.elements[ 1 ] *= invSX; + _m1$2.elements[ 2 ] *= invSX; - } else { + _m1$2.elements[ 4 ] *= invSY; + _m1$2.elements[ 5 ] *= invSY; + _m1$2.elements[ 6 ] *= invSY; - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + _m1$2.elements[ 8 ] *= invSZ; + _m1$2.elements[ 9 ] *= invSZ; + _m1$2.elements[ 10 ] *= invSZ; - } + quaternion.setFromRotationMatrix( _m1$2 ); - // update children + scale.x = sx; + scale.y = sy; + scale.z = sz; - if ( updateChildren === true ) { + return this; - const children = this.children; + } - for ( let i = 0, l = children.length; i < l; i ++ ) { + makePerspective( left, right, top, bottom, near, far ) { - children[ i ].updateWorldMatrix( false, true ); + if ( far === undefined ) { - } + console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); } - } - - toJSON( meta ) { - - // meta is a string when called from JSON.stringify - const isRootObject = ( meta === undefined || typeof meta === 'string' ); + const te = this.elements; + const x = 2 * near / ( right - left ); + const y = 2 * near / ( top - bottom ); - const output = {}; + const a = ( right + left ) / ( right - left ); + const b = ( top + bottom ) / ( top - bottom ); + const c = - ( far + near ) / ( far - near ); + const d = - 2 * far * near / ( far - near ); - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { + 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; - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {}, - skeletons: {}, - animations: {} - }; + return this; - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; + } - } + makeOrthographic( left, right, top, bottom, near, far ) { - // standard Object3D serialization + const te = this.elements; + const w = 1.0 / ( right - left ); + const h = 1.0 / ( top - bottom ); + const p = 1.0 / ( far - near ); - const object = {}; + const x = ( right + left ) * w; + const y = ( top + bottom ) * h; + const z = ( far + near ) * p; - object.uuid = this.uuid; - object.type = this.type; + 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; - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + return this; - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); + } - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; + equals( matrix ) { - // object specific properties + const te = this.elements; + const me = matrix.elements; - if ( this.isInstancedMesh ) { + for ( let i = 0; i < 16; i ++ ) { - object.type = 'InstancedMesh'; - object.count = this.count; - object.instanceMatrix = this.instanceMatrix.toJSON(); - if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); + if ( te[ i ] !== me[ i ] ) return false; } - // - - function serialize( library, element ) { + return true; - if ( library[ element.uuid ] === undefined ) { + } - library[ element.uuid ] = element.toJSON( meta ); + fromArray( array, offset = 0 ) { - } + for ( let i = 0; i < 16; i ++ ) { - return element.uuid; + this.elements[ i ] = array[ i + offset ]; } - if ( this.isMesh || this.isLine || this.isPoints ) { - - object.geometry = serialize( meta.geometries, this.geometry ); + return this; - const parameters = this.geometry.parameters; + } - if ( parameters !== undefined && parameters.shapes !== undefined ) { + toArray( array = [], offset = 0 ) { - const shapes = parameters.shapes; + const te = this.elements; - if ( Array.isArray( shapes ) ) { + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; - for ( let i = 0, l = shapes.length; i < l; i ++ ) { - - const shape = shapes[ i ]; - - serialize( meta.shapes, shape ); + 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 ]; - } else { + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; - serialize( meta.shapes, shapes ); + return array; - } + } - } +} - } +Matrix4.prototype.isMatrix4 = true; - if ( this.isSkinnedMesh ) { +const _v1$5 = /*@__PURE__*/ new Vector3(); +const _m1$2 = /*@__PURE__*/ new Matrix4(); +const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); +const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); +const _x = /*@__PURE__*/ new Vector3(); +const _y = /*@__PURE__*/ new Vector3(); +const _z = /*@__PURE__*/ new Vector3(); - object.bindMode = this.bindMode; - object.bindMatrix = this.bindMatrix.toArray(); +const _matrix$1 = /*@__PURE__*/ new Matrix4(); +const _quaternion$3 = /*@__PURE__*/ new Quaternion(); - if ( this.skeleton !== undefined ) { +class Euler { - serialize( meta.skeletons, this.skeleton ); + constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) { - object.skeleton = this.skeleton.uuid; + this._x = x; + this._y = y; + this._z = z; + this._order = order; - } + } - } + get x() { - if ( this.material !== undefined ) { + return this._x; - if ( Array.isArray( this.material ) ) { + } - const uuids = []; + set x( value ) { - for ( let i = 0, l = this.material.length; i < l; i ++ ) { + this._x = value; + this._onChangeCallback(); - uuids.push( serialize( meta.materials, this.material[ i ] ) ); + } - } + get y() { - object.material = uuids; + return this._y; - } else { + } - object.material = serialize( meta.materials, this.material ); + set y( value ) { - } + this._y = value; + this._onChangeCallback(); - } + } - // + get z() { - if ( this.children.length > 0 ) { + return this._z; - object.children = []; + } - for ( let i = 0; i < this.children.length; i ++ ) { + set z( value ) { - object.children.push( this.children[ i ].toJSON( meta ).object ); + this._z = value; + this._onChangeCallback(); - } + } - } + get order() { - // + return this._order; - if ( this.animations.length > 0 ) { + } - object.animations = []; + set order( value ) { - for ( let i = 0; i < this.animations.length; i ++ ) { + this._order = value; + this._onChangeCallback(); - const animation = this.animations[ i ]; + } - object.animations.push( serialize( meta.animations, animation ) ); + set( x, y, z, order = this._order ) { - } + this._x = x; + this._y = y; + this._z = z; + this._order = order; - } + this._onChangeCallback(); - if ( isRootObject ) { + return this; - const geometries = extractFromCache( meta.geometries ); - const materials = extractFromCache( meta.materials ); - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); - const shapes = extractFromCache( meta.shapes ); - const skeletons = extractFromCache( meta.skeletons ); - const animations = extractFromCache( meta.animations ); + } - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; - if ( skeletons.length > 0 ) output.skeletons = skeletons; - if ( animations.length > 0 ) output.animations = animations; + clone() { - } + return new this.constructor( this._x, this._y, this._z, this._order ); - output.object = object; + } - return output; + copy( euler ) { - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; - const values = []; - for ( const key in cache ) { + this._onChangeCallback(); - const data = cache[ key ]; - delete data.metadata; - values.push( data ); + return this; - } + } - return values; + setFromRotationMatrix( m, order = this._order, update = true ) { - } + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - } + const te = m.elements; + const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - clone( recursive ) { + switch ( order ) { - return new this.constructor().copy( this, recursive ); + case 'XYZ': - } + this._y = Math.asin( clamp( m13, - 1, 1 ) ); - copy( source, recursive = true ) { + if ( Math.abs( m13 ) < 0.9999999 ) { - this.name = source.name; + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); - this.up.copy( source.up ); + } else { - this.position.copy( source.position ); - this.rotation.order = source.rotation.order; - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); + this._x = Math.atan2( m32, m22 ); + this._z = 0; - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); + } - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + break; - this.layers.mask = source.layers.mask; - this.visible = source.visible; + case 'YXZ': - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; + if ( Math.abs( m23 ) < 0.9999999 ) { - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); - if ( recursive === true ) { + } else { - for ( let i = 0; i < source.children.length; i ++ ) { + this._y = Math.atan2( - m31, m11 ); + this._z = 0; - const child = source.children[ i ]; - this.add( child.clone() ); + } - } + break; - } + case 'ZXY': - return this; + this._x = Math.asin( clamp( m32, - 1, 1 ) ); - } + if ( Math.abs( m32 ) < 0.9999999 ) { -} + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); -Object3D.DefaultUp = new Vector3( 0, 1, 0 ); -Object3D.DefaultMatrixAutoUpdate = true; + } else { -Object3D.prototype.isObject3D = true; + this._y = 0; + this._z = Math.atan2( m21, m11 ); -const _vector1 = /*@__PURE__*/ new Vector3(); -const _vector2$1 = /*@__PURE__*/ new Vector3(); -const _normalMatrix = /*@__PURE__*/ new Matrix3(); + } -class Plane { + break; - constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { + case 'ZYX': - // normal is assumed to be normalized + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - this.normal = normal; - this.constant = constant; + if ( Math.abs( m31 ) < 0.9999999 ) { - } + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); - set( normal, constant ) { + } else { - this.normal.copy( normal ); - this.constant = constant; + this._x = 0; + this._z = Math.atan2( - m12, m22 ); - return this; + } - } + break; - setComponents( x, y, z, w ) { + case 'YZX': - this.normal.set( x, y, z ); - this.constant = w; + this._z = Math.asin( clamp( m21, - 1, 1 ) ); - return this; + if ( Math.abs( m21 ) < 0.9999999 ) { - } + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); - setFromNormalAndCoplanarPoint( normal, point ) { + } else { - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); + this._x = 0; + this._y = Math.atan2( m13, m33 ); - return this; + } - } + break; - setFromCoplanarPoints( a, b, c ) { + case 'XZY': - const normal = _vector1.subVectors( c, b ).cross( _vector2$1.subVectors( a, b ) ).normalize(); + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + if ( Math.abs( m12 ) < 0.9999999 ) { - this.setFromNormalAndCoplanarPoint( normal, a ); + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); - return this; + } else { - } + this._x = Math.atan2( - m23, m33 ); + this._y = 0; - copy( plane ) { + } - this.normal.copy( plane.normal ); - this.constant = plane.constant; + break; - return this; + default: - } + console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - normalize() { + } - // Note: will lead to a divide by zero if the plane is invalid. + this._order = order; - const inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; + if ( update === true ) this._onChangeCallback(); return this; } - negate() { + setFromQuaternion( q, order, update ) { - this.constant *= - 1; - this.normal.negate(); + _matrix$1.makeRotationFromQuaternion( q ); - return this; + return this.setFromRotationMatrix( _matrix$1, order, update ); } - distanceToPoint( point ) { + setFromVector3( v, order = this._order ) { - return this.normal.dot( point ) + this.constant; + return this.set( v.x, v.y, v.z, order ); } - distanceToSphere( sphere ) { - - return this.distanceToPoint( sphere.center ) - sphere.radius; + reorder( newOrder ) { - } + // WARNING: this discards revolution information -bhouston - projectPoint( point, target ) { + _quaternion$3.setFromEuler( this ); - if ( target === undefined ) { + return this.setFromQuaternion( _quaternion$3, newOrder ); - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); + } - } + equals( euler ) { - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); } - intersectLine( line, target ) { + fromArray( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - if ( target === undefined ) { + this._onChangeCallback(); - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); + return this; - } + } - const direction = line.delta( _vector1 ); + toArray( array = [], offset = 0 ) { - const denominator = this.normal.dot( direction ); + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._order; - if ( denominator === 0 ) { + return array; - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { + } - return target.copy( line.start ); + _onChange( callback ) { - } + this._onChangeCallback = callback; - // Unsure if this is the correct method to handle this case. - return null; + return this; - } + } - const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + _onChangeCallback() {} - if ( t < 0 || t > 1 ) { +} - return null; +Euler.prototype.isEuler = true; - } +Euler.DefaultOrder = 'XYZ'; +Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; - return target.copy( direction ).multiplyScalar( t ).add( line.start ); +class Layers { - } + constructor() { - intersectsLine( line ) { + this.mask = 1 | 0; - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + } - const startSign = this.distanceToPoint( line.start ); - const endSign = this.distanceToPoint( line.end ); + set( channel ) { - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + this.mask = ( 1 << channel | 0 ) >>> 0; } - intersectsBox( box ) { + enable( channel ) { - return box.intersectsPlane( this ); + this.mask |= 1 << channel | 0; } - intersectsSphere( sphere ) { + enableAll() { - return sphere.intersectsPlane( this ); + this.mask = 0xffffffff | 0; } - coplanarPoint( target ) { + toggle( channel ) { - if ( target === undefined ) { + this.mask ^= 1 << channel | 0; - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); + } - } + disable( channel ) { - return target.copy( this.normal ).multiplyScalar( - this.constant ); + this.mask &= ~ ( 1 << channel | 0 ); } - applyMatrix4( matrix, optionalNormalMatrix ) { - - const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); + disableAll() { - const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); + this.mask = 0; - const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + } - this.constant = - referencePoint.dot( normal ); + test( layers ) { - return this; + return ( this.mask & layers.mask ) !== 0; } - translate( offset ) { - - this.constant -= offset.dot( this.normal ); + isEnabled( channel ) { - return this; + return ( this.mask & ( 1 << channel | 0 ) ) !== 0; } - equals( plane ) { +} - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); +let _object3DId = 0; - } +const _v1$4 = /*@__PURE__*/ new Vector3(); +const _q1 = /*@__PURE__*/ new Quaternion(); +const _m1$1 = /*@__PURE__*/ new Matrix4(); +const _target = /*@__PURE__*/ new Vector3(); - clone() { +const _position$3 = /*@__PURE__*/ new Vector3(); +const _scale$2 = /*@__PURE__*/ new Vector3(); +const _quaternion$2 = /*@__PURE__*/ new Quaternion(); - return new this.constructor().copy( this ); +const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); +const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); +const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); - } +const _addedEvent = { type: 'added' }; +const _removedEvent = { type: 'removed' }; -} +class Object3D extends EventDispatcher$1 { -Plane.prototype.isPlane = true; + constructor() { -const _v0$1 = /*@__PURE__*/ new Vector3(); -const _v1$3 = /*@__PURE__*/ new Vector3(); -const _v2$2 = /*@__PURE__*/ new Vector3(); -const _v3$1 = /*@__PURE__*/ new Vector3(); + super(); -const _vab = /*@__PURE__*/ new Vector3(); -const _vac = /*@__PURE__*/ new Vector3(); -const _vbc = /*@__PURE__*/ new Vector3(); -const _vap = /*@__PURE__*/ new Vector3(); -const _vbp = /*@__PURE__*/ new Vector3(); -const _vcp = /*@__PURE__*/ new Vector3(); + Object.defineProperty( this, 'id', { value: _object3DId ++ } ); -class Triangle { + this.uuid = generateUUID(); - constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { + this.name = ''; + this.type = 'Object3D'; - this.a = a; - this.b = b; - this.c = c; + this.parent = null; + this.children = []; - } + this.up = Object3D.DefaultUp.clone(); - static getNormal( a, b, c, target ) { + const position = new Vector3(); + const rotation = new Euler(); + const quaternion = new Quaternion(); + const scale = new Vector3( 1, 1, 1 ); - if ( target === undefined ) { + function onRotationChange() { - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); + quaternion.setFromEuler( rotation, false ); } - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); - - const targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { + function onQuaternionChange() { - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); + rotation.setFromQuaternion( quaternion, undefined, false ); } - return target.set( 0, 0, 0 ); + rotation._onChange( onRotationChange ); + quaternion._onChange( onQuaternionChange ); - } + Object.defineProperties( this, { + position: { + configurable: true, + enumerable: true, + value: position + }, + rotation: { + configurable: true, + enumerable: true, + value: rotation + }, + quaternion: { + configurable: true, + enumerable: true, + value: quaternion + }, + scale: { + configurable: true, + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new Matrix4() + }, + normalMatrix: { + value: new Matrix3() + } + } ); - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - static getBarycoord( point, a, b, c, target ) { + this.matrix = new Matrix4(); + this.matrixWorld = new Matrix4(); - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$2.subVectors( point, a ); + this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; + this.matrixWorldNeedsUpdate = false; - const dot00 = _v0$1.dot( _v0$1 ); - const dot01 = _v0$1.dot( _v1$3 ); - const dot02 = _v0$1.dot( _v2$2 ); - const dot11 = _v1$3.dot( _v1$3 ); - const dot12 = _v1$3.dot( _v2$2 ); + this.layers = new Layers(); + this.visible = true; - const denom = ( dot00 * dot11 - dot01 * dot01 ); + this.castShadow = false; + this.receiveShadow = false; - if ( target === undefined ) { + this.frustumCulled = true; + this.renderOrder = 0; - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); + this.animations = []; - } + this.userData = {}; - // collinear or singular triangle - if ( denom === 0 ) { + } - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); + onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} - } + onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} - const invDenom = 1 / denom; - const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + applyMatrix4( matrix ) { - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); + if ( this.matrixAutoUpdate ) this.updateMatrix(); + + this.matrix.premultiply( matrix ); + + this.matrix.decompose( this.position, this.quaternion, this.scale ); } - static containsPoint( point, a, b, c ) { + applyQuaternion( q ) { - this.getBarycoord( point, a, b, c, _v3$1 ); + this.quaternion.premultiply( q ); - return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); + return this; } - static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - - this.getBarycoord( point, p1, p2, p3, _v3$1 ); + setRotationFromAxisAngle( axis, angle ) { - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3$1.x ); - target.addScaledVector( uv2, _v3$1.y ); - target.addScaledVector( uv3, _v3$1.z ); + // assumes axis is normalized - return target; + this.quaternion.setFromAxisAngle( axis, angle ); } - static isFrontFacing( a, b, c, direction ) { - - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); + setRotationFromEuler( euler ) { - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; + this.quaternion.setFromEuler( euler, true ); } - set( a, b, c ) { + setRotationFromMatrix( m ) { - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - return this; + this.quaternion.setFromRotationMatrix( m ); } - setFromPointsAndIndices( points, i0, i1, i2 ) { + setRotationFromQuaternion( q ) { - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); + // assumes q is normalized - return this; + this.quaternion.copy( q ); } - clone() { - - return new this.constructor().copy( this ); + rotateOnAxis( axis, angle ) { - } + // rotate object on axis in object space + // axis is assumed to be normalized - copy( triangle ) { + _q1.setFromAxisAngle( axis, angle ); - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); + this.quaternion.multiply( _q1 ); return this; } - getArea() { + rotateOnWorldAxis( axis, angle ) { - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); + // rotate object on axis in world space + // axis is assumed to be normalized + // method assumes no rotated parent - return _v0$1.cross( _v1$3 ).length() * 0.5; + _q1.setFromAxisAngle( axis, angle ); + + this.quaternion.premultiply( _q1 ); + + return this; } - getMidpoint( target ) { + rotateX( angle ) { - if ( target === undefined ) { + return this.rotateOnAxis( _xAxis, angle ); - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); + } - } + rotateY( angle ) { - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + return this.rotateOnAxis( _yAxis, angle ); } - getNormal( target ) { + rotateZ( angle ) { - return Triangle.getNormal( this.a, this.b, this.c, target ); + return this.rotateOnAxis( _zAxis, angle ); } - getPlane( target ) { + translateOnAxis( axis, distance ) { - if ( target === undefined ) { + // translate object by distance along axis in object space + // axis is assumed to be normalized - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Plane(); + _v1$4.copy( axis ).applyQuaternion( this.quaternion ); - } + this.position.add( _v1$4.multiplyScalar( distance ) ); - return target.setFromCoplanarPoints( this.a, this.b, this.c ); + return this; } - getBarycoord( point, target ) { + translateX( distance ) { - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); + return this.translateOnAxis( _xAxis, distance ); } - getUV( point, uv1, uv2, uv3, target ) { + translateY( distance ) { - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); + return this.translateOnAxis( _yAxis, distance ); } - containsPoint( point ) { + translateZ( distance ) { - return Triangle.containsPoint( point, this.a, this.b, this.c ); + return this.translateOnAxis( _zAxis, distance ); } - isFrontFacing( direction ) { + localToWorld( vector ) { - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); + return vector.applyMatrix4( this.matrixWorld ); } - intersectsBox( box ) { + worldToLocal( vector ) { - return box.intersectsTriangle( this ); + return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); } - closestPointToPoint( p, target ) { + lookAt( x, y, z ) { - if ( target === undefined ) { + // This method does not support objects having non-uniformly-scaled parent(s) - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); + if ( x.isVector3 ) { - } + _target.copy( x ); - const a = this.a, b = this.b, c = this.c; - let v, w; + } else { - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. + _target.set( x, y, z ); - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - const d1 = _vab.dot( _vap ); - const d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { + } - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); + const parent = this.parent; - } + this.updateWorldMatrix( true, false ); - _vbp.subVectors( p, b ); - const d3 = _vab.dot( _vbp ); - const d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { + _position$3.setFromMatrixPosition( this.matrixWorld ); - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); + if ( this.isCamera || this.isLight ) { - } + _m1$1.lookAt( _position$3, _target, this.up ); - const vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { + } else { - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); + _m1$1.lookAt( _target, _position$3, this.up ); } - _vcp.subVectors( p, c ); - const d5 = _vab.dot( _vcp ); - const d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { + this.quaternion.setFromRotationMatrix( _m1$1 ); - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); + if ( parent ) { - } + _m1$1.extractRotation( parent.matrixWorld ); + _q1.setFromRotationMatrix( _m1$1 ); + this.quaternion.premultiply( _q1.invert() ); - const vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { + } - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); + } - } + add( object ) { - const va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { + if ( arguments.length > 1 ) { - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC + for ( let i = 0; i < arguments.length; i ++ ) { - } + this.add( arguments[ i ] ); - // face region - const denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; + } - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); + return this; - } + } - equals( triangle ) { + if ( object === this ) { - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); + return this; - } + } -} + if ( object && object.isObject3D ) { -let materialId = 0; + if ( object.parent !== null ) { -function Material() { + object.parent.remove( object ); - Object.defineProperty( this, 'id', { value: materialId ++ } ); + } - this.uuid = generateUUID(); + object.parent = this; + this.children.push( object ); - this.name = ''; - this.type = 'Material'; + object.dispatchEvent( _addedEvent ); - this.fog = true; + } else { - this.blending = NormalBlending; - this.side = FrontSide; - this.vertexColors = false; + console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); - this.opacity = 1; - this.transparent = false; + } - this.blendSrc = SrcAlphaFactor; - this.blendDst = OneMinusSrcAlphaFactor; - this.blendEquation = AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; + return this; - this.depthFunc = LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; + } - this.stencilWriteMask = 0xff; - this.stencilFunc = AlwaysStencilFunc; - this.stencilRef = 0; - this.stencilFuncMask = 0xff; - this.stencilFail = KeepStencilOp; - this.stencilZFail = KeepStencilOp; - this.stencilZPass = KeepStencilOp; - this.stencilWrite = false; + remove( object ) { - this.clippingPlanes = null; - this.clipIntersection = false; - this.clipShadows = false; + if ( arguments.length > 1 ) { - this.shadowSide = null; + for ( let i = 0; i < arguments.length; i ++ ) { - this.colorWrite = true; + this.remove( arguments[ i ] ); - this.precision = null; // override the renderer's default precision for this material + } - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; + return this; - this.dithering = false; + } - this.alphaTest = 0; - this.alphaToCoverage = false; - this.premultipliedAlpha = false; + const index = this.children.indexOf( object ); - this.visible = true; + if ( index !== - 1 ) { - this.toneMapped = true; + object.parent = null; + this.children.splice( index, 1 ); - this.userData = {}; + object.dispatchEvent( _removedEvent ); - this.version = 0; + } -} + return this; -Material.prototype = Object.assign( Object.create( EventDispatcher$1.prototype ), { + } - constructor: Material, + removeFromParent() { - isMaterial: true, + const parent = this.parent; - onBuild: function ( /* shaderobject, renderer */ ) {}, + if ( parent !== null ) { - onBeforeCompile: function ( /* shaderobject, renderer */ ) {}, + parent.remove( this ); - customProgramCacheKey: function () { + } - return this.onBeforeCompile.toString(); + return this; - }, + } - setValues: function ( values ) { + clear() { - if ( values === undefined ) return; + for ( let i = 0; i < this.children.length; i ++ ) { - for ( const key in values ) { + const object = this.children[ i ]; - const newValue = values[ key ]; + object.parent = null; - if ( newValue === undefined ) { + object.dispatchEvent( _removedEvent ); - console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); - continue; + } - } + this.children.length = 0; - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { + return this; - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; - } + } - const currentValue = this[ key ]; + attach( object ) { - if ( currentValue === undefined ) { + // adds object as a child of this, while maintaining the object's world transform - console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); - continue; + // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) - } + this.updateWorldMatrix( true, false ); - if ( currentValue && currentValue.isColor ) { + _m1$1.copy( this.matrixWorld ).invert(); - currentValue.set( newValue ); + if ( object.parent !== null ) { - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { + object.parent.updateWorldMatrix( true, false ); - currentValue.copy( newValue ); + _m1$1.multiply( object.parent.matrixWorld ); - } else { + } - this[ key ] = newValue; + object.applyMatrix4( _m1$1 ); - } + this.add( object ); - } + object.updateWorldMatrix( false, true ); - }, + return this; - toJSON: function ( meta ) { + } - const isRoot = ( meta === undefined || typeof meta === 'string' ); + getObjectById( id ) { - if ( isRoot ) { + return this.getObjectByProperty( 'id', id ); - meta = { - textures: {}, - images: {} - }; + } - } + getObjectByName( name ) { - const data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; + return this.getObjectByProperty( 'name', name ); - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; + } - if ( this.name !== '' ) data.name = this.name; + getObjectByProperty( name, value ) { - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); + if ( this[ name ] === value ) return this; - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; + for ( let i = 0, l = this.children.length; i < l; i ++ ) { - if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex(); - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; + const child = this.children[ i ]; + const object = child.getObjectByProperty( name, value ); - 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 ( object !== undefined ) { - if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { + return object; - data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; + } } - if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { + return undefined; - data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; + } - } + getWorldPosition( target ) { - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { + this.updateWorldMatrix( true, false ); - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); + return target.setFromMatrixPosition( this.matrixWorld ); - } + } - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + getWorldQuaternion( target ) { - if ( this.lightMap && this.lightMap.isTexture ) { + this.updateWorldMatrix( true, false ); - data.lightMap = this.lightMap.toJSON( meta ).uuid; - data.lightMapIntensity = this.lightMapIntensity; + this.matrixWorld.decompose( _position$3, target, _scale$2 ); - } + return target; - if ( this.aoMap && this.aoMap.isTexture ) { + } - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; + getWorldScale( target ) { - } + this.updateWorldMatrix( true, false ); - if ( this.bumpMap && this.bumpMap.isTexture ) { + this.matrixWorld.decompose( _position$3, _quaternion$2, target ); - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; + return target; - } + } - if ( this.normalMap && this.normalMap.isTexture ) { + getWorldDirection( target ) { - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); + this.updateWorldMatrix( true, false ); - } + const e = this.matrixWorld.elements; - if ( this.displacementMap && this.displacementMap.isTexture ) { + return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; + } - } + raycast( /* raycaster, intersects */ ) {} - 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; + traverse( callback ) { - 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; + callback( this ); - if ( this.envMap && this.envMap.isTexture ) { + const children = this.children; - data.envMap = this.envMap.toJSON( meta ).uuid; + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( this.combine !== undefined ) data.combine = this.combine; + children[ i ].traverse( callback ); } - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; - if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; - if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; - - if ( this.gradientMap && this.gradientMap.isTexture ) { + } - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; + traverseVisible( callback ) { - } + if ( this.visible === false ) return; - if ( this.size !== undefined ) data.size = this.size; - if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + callback( this ); - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors ) data.vertexColors = true; + const children = this.children; - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; + for ( let i = 0, l = children.length; i < l; i ++ ) { - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; - data.colorWrite = this.colorWrite; + children[ i ].traverseVisible( callback ); - data.stencilWrite = this.stencilWrite; - data.stencilWriteMask = this.stencilWriteMask; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilFuncMask = this.stencilFuncMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; + } - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; + } - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; + traverseAncestors( callback ) { - if ( this.linewidth && 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; + const parent = this.parent; - if ( this.dithering === true ) data.dithering = true; + if ( parent !== null ) { - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; + callback( parent ); - 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; + parent.traverseAncestors( callback ); - if ( this.morphTargets === true ) data.morphTargets = true; - if ( this.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; + } - if ( this.flatShading === true ) data.flatShading = this.flatShading; + } - if ( this.visible === false ) data.visible = false; + updateMatrix() { - if ( this.toneMapped === false ) data.toneMapped = false; + this.matrix.compose( this.position, this.quaternion, this.scale ); - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; + this.matrixWorldNeedsUpdate = true; - // TODO: Copied from Object3D.toJSON + } - function extractFromCache( cache ) { + updateMatrixWorld( force ) { - const values = []; + if ( this.matrixAutoUpdate ) this.updateMatrix(); - for ( const key in cache ) { + if ( this.matrixWorldNeedsUpdate || force ) { - const data = cache[ key ]; - delete data.metadata; - values.push( data ); + if ( this.parent === null ) { - } + this.matrixWorld.copy( this.matrix ); - return values; + } else { - } + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - if ( isRoot ) { + } - const textures = extractFromCache( meta.textures ); - const images = extractFromCache( meta.images ); + this.matrixWorldNeedsUpdate = false; - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; + force = true; } - return data; + // update children - }, + const children = this.children; - clone: function () { + for ( let i = 0, l = children.length; i < l; i ++ ) { - return new this.constructor().copy( this ); + children[ i ].updateMatrixWorld( force ); - }, + } - copy: function ( source ) { + } - this.name = source.name; + updateWorldMatrix( updateParents, updateChildren ) { - this.fog = source.fog; + const parent = this.parent; - this.blending = source.blending; - this.side = source.side; - this.vertexColors = source.vertexColors; + if ( updateParents === true && parent !== null ) { - this.opacity = source.opacity; - this.transparent = source.transparent; + parent.updateWorldMatrix( true, false ); - 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; + if ( this.matrixAutoUpdate ) this.updateMatrix(); - this.stencilWriteMask = source.stencilWriteMask; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilFuncMask = source.stencilFuncMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; - this.stencilWrite = source.stencilWrite; + if ( this.parent === null ) { - const srcPlanes = source.clippingPlanes; - let dstPlanes = null; + this.matrixWorld.copy( this.matrix ); - if ( srcPlanes !== null ) { + } else { - const n = srcPlanes.length; - dstPlanes = new Array( n ); + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - for ( let i = 0; i !== n; ++ i ) { + } - dstPlanes[ i ] = srcPlanes[ i ].clone(); + // update children - } + if ( updateChildren === true ) { - } + const children = this.children; - this.clippingPlanes = dstPlanes; - this.clipIntersection = source.clipIntersection; - this.clipShadows = source.clipShadows; + for ( let i = 0, l = children.length; i < l; i ++ ) { - this.shadowSide = source.shadowSide; + children[ i ].updateWorldMatrix( false, true ); - this.colorWrite = source.colorWrite; + } - this.precision = source.precision; + } - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; + } - this.dithering = source.dithering; + toJSON( meta ) { - this.alphaTest = source.alphaTest; - this.alphaToCoverage = source.alphaToCoverage; - this.premultipliedAlpha = source.premultipliedAlpha; + // meta is a string when called from JSON.stringify + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - this.visible = source.visible; + const output = {}; - this.toneMapped = source.toneMapped; + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {}, + shapes: {}, + skeletons: {}, + animations: {}, + nodes: {} + }; - return this; + output.metadata = { + version: 4.5, + type: 'Object', + generator: 'Object3D.toJSON' + }; - }, + } - dispose: function () { + // standard Object3D serialization - this.dispatchEvent( { type: 'dispose' } ); + const object = {}; - } + object.uuid = this.uuid; + object.type = this.type; -} ); + if ( this.name !== '' ) object.name = this.name; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; + if ( this.frustumCulled === false ) object.frustumCulled = false; + if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; + if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; -Object.defineProperty( Material.prototype, 'needsUpdate', { + object.layers = this.layers.mask; + object.matrix = this.matrix.toArray(); - set: function ( value ) { + if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - if ( value === true ) this.version ++; + // object specific properties - } + if ( this.isInstancedMesh ) { -} ); + object.type = 'InstancedMesh'; + object.count = this.count; + object.instanceMatrix = this.instanceMatrix.toJSON(); + if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); -const _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 }; + } -const _hslA = { h: 0, s: 0, l: 0 }; -const _hslB = { h: 0, s: 0, l: 0 }; + // -function hue2rgb( p, q, t ) { + function serialize( library, element ) { - 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; + if ( library[ element.uuid ] === undefined ) { -} + library[ element.uuid ] = element.toJSON( meta ); -function SRGBToLinear( c ) { + } - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); + return element.uuid; -} + } -function LinearToSRGB( c ) { + if ( this.isScene ) { - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; + if ( this.background ) { -} + if ( this.background.isColor ) { -class Color { + object.background = this.background.toJSON(); - constructor( r, g, b ) { + } else if ( this.background.isTexture ) { - if ( g === undefined && b === undefined ) { + object.background = this.background.toJSON( meta ).uuid; - // r is THREE.Color, hex or string - return this.set( r ); + } - } + } - return this.setRGB( r, g, b ); + if ( this.environment && this.environment.isTexture ) { - } + object.environment = this.environment.toJSON( meta ).uuid; - set( value ) { + } - if ( value && value.isColor ) { + } else if ( this.isMesh || this.isLine || this.isPoints ) { - this.copy( value ); + object.geometry = serialize( meta.geometries, this.geometry ); - } else if ( typeof value === 'number' ) { + const parameters = this.geometry.parameters; - this.setHex( value ); + if ( parameters !== undefined && parameters.shapes !== undefined ) { - } else if ( typeof value === 'string' ) { + const shapes = parameters.shapes; - this.setStyle( value ); + if ( Array.isArray( shapes ) ) { - } + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - return this; + const shape = shapes[ i ]; - } + serialize( meta.shapes, shape ); - setScalar( scalar ) { + } - this.r = scalar; - this.g = scalar; - this.b = scalar; + } else { - return this; + serialize( meta.shapes, shapes ); - } + } - setHex( hex ) { + } - hex = Math.floor( hex ); + } - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; + if ( this.isSkinnedMesh ) { - return this; + object.bindMode = this.bindMode; + object.bindMatrix = this.bindMatrix.toArray(); - } + if ( this.skeleton !== undefined ) { - setRGB( r, g, b ) { + serialize( meta.skeletons, this.skeleton ); - this.r = r; - this.g = g; - this.b = b; + object.skeleton = this.skeleton.uuid; - return this; + } - } + } - setHSL( h, s, l ) { + if ( this.material !== undefined ) { - // h,s,l ranges are in 0.0 - 1.0 - h = euclideanModulo( h, 1 ); - s = clamp( s, 0, 1 ); - l = clamp( l, 0, 1 ); + if ( Array.isArray( this.material ) ) { - if ( s === 0 ) { + const uuids = []; - this.r = this.g = this.b = l; + for ( let i = 0, l = this.material.length; i < l; i ++ ) { - } else { + uuids.push( serialize( meta.materials, this.material[ i ] ) ); - const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - const 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 ); + object.material = uuids; - } + } else { - return this; + object.material = serialize( meta.materials, this.material ); - } + } - setStyle( style ) { + } - function handleAlpha( string ) { + // - if ( string === undefined ) return; + if ( this.children.length > 0 ) { - if ( parseFloat( string ) < 1 ) { + object.children = []; - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + for ( let i = 0; i < this.children.length; i ++ ) { + + object.children.push( this.children[ i ].toJSON( meta ).object ); } } + // - let m; - - if ( m = /^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec( style ) ) { + if ( this.animations.length > 0 ) { - // rgb / hsl + object.animations = []; - let color; - const name = m[ 1 ]; - const components = m[ 2 ]; + for ( let i = 0; i < this.animations.length; i ++ ) { - switch ( name ) { + const animation = this.animations[ i ]; - case 'rgb': - case 'rgba': + object.animations.push( serialize( meta.animations, animation ) ); - if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\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[ 4 ] ); + if ( isRootObject ) { - return this; + const geometries = extractFromCache( meta.geometries ); + const materials = extractFromCache( meta.materials ); + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); + const shapes = extractFromCache( meta.shapes ); + const skeletons = extractFromCache( meta.skeletons ); + const animations = extractFromCache( meta.animations ); + const nodes = extractFromCache( meta.nodes ); - } + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + if ( shapes.length > 0 ) output.shapes = shapes; + if ( skeletons.length > 0 ) output.skeletons = skeletons; + if ( animations.length > 0 ) output.animations = animations; + if ( nodes.length > 0 ) output.nodes = nodes; - if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\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; + output.object = object; - handleAlpha( color[ 4 ] ); + return output; - return this; + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache( cache ) { - } + const values = []; + for ( const key in cache ) { - break; + const data = cache[ key ]; + delete data.metadata; + values.push( data ); - case 'hsl': - case 'hsla': + } - if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { + return values; - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - const h = parseFloat( color[ 1 ] ) / 360; - const s = parseInt( color[ 2 ], 10 ) / 100; - const l = parseInt( color[ 3 ], 10 ) / 100; + } - handleAlpha( color[ 4 ] ); + } - return this.setHSL( h, s, l ); + clone( recursive ) { - } + return new this.constructor().copy( this, recursive ); - break; + } - } + copy( source, recursive = true ) { - } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { + this.name = source.name; - // hex color + this.up.copy( source.up ); - const hex = m[ 1 ]; - const size = hex.length; + this.position.copy( source.position ); + this.rotation.order = source.rotation.order; + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); - if ( size === 3 ) { + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); - // #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; + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; - return this; + this.layers.mask = source.layers.mask; + this.visible = source.visible; - } else if ( size === 6 ) { + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; - // #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; + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; - return this; + this.userData = JSON.parse( JSON.stringify( source.userData ) ); - } + if ( recursive === true ) { - } + for ( let i = 0; i < source.children.length; i ++ ) { - if ( style && style.length > 0 ) { + const child = source.children[ i ]; + this.add( child.clone() ); - return this.setColorName( style ); + } } @@ -10059,630 +9833,1039 @@ class Color { } - setColorName( style ) { +} - // color keywords - const hex = _colorKeywords[ style.toLowerCase() ]; +Object3D.DefaultUp = new Vector3( 0, 1, 0 ); +Object3D.DefaultMatrixAutoUpdate = true; - if ( hex !== undefined ) { +Object3D.prototype.isObject3D = true; - // red - this.setHex( hex ); +const _v0$1 = /*@__PURE__*/ new Vector3(); +const _v1$3 = /*@__PURE__*/ new Vector3(); +const _v2$2 = /*@__PURE__*/ new Vector3(); +const _v3$1 = /*@__PURE__*/ new Vector3(); - } else { +const _vab = /*@__PURE__*/ new Vector3(); +const _vac = /*@__PURE__*/ new Vector3(); +const _vbc = /*@__PURE__*/ new Vector3(); +const _vap = /*@__PURE__*/ new Vector3(); +const _vbp = /*@__PURE__*/ new Vector3(); +const _vcp = /*@__PURE__*/ new Vector3(); - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); +class Triangle { - } + constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { - return this; + this.a = a; + this.b = b; + this.c = c; } - clone() { + static getNormal( a, b, c, target ) { - return new this.constructor( this.r, this.g, this.b ); + target.subVectors( c, b ); + _v0$1.subVectors( a, b ); + target.cross( _v0$1 ); - } + const targetLengthSq = target.lengthSq(); + if ( targetLengthSq > 0 ) { - copy( color ) { + return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - this.r = color.r; - this.g = color.g; - this.b = color.b; + } - return this; + return target.set( 0, 0, 0 ); } - copyGammaToLinear( color, gammaFactor = 2.0 ) { + // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + static getBarycoord( point, a, b, c, target ) { + + _v0$1.subVectors( c, a ); + _v1$3.subVectors( b, a ); + _v2$2.subVectors( point, a ); - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); + const dot00 = _v0$1.dot( _v0$1 ); + const dot01 = _v0$1.dot( _v1$3 ); + const dot02 = _v0$1.dot( _v2$2 ); + const dot11 = _v1$3.dot( _v1$3 ); + const dot12 = _v1$3.dot( _v2$2 ); - return this; + const denom = ( dot00 * dot11 - dot01 * dot01 ); - } + // collinear or singular triangle + if ( denom === 0 ) { - copyLinearToGamma( color, gammaFactor = 2.0 ) { + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return target.set( - 2, - 1, - 1 ); - const 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 ); + const invDenom = 1 / denom; + const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - return this; + // barycentric coordinates must always sum to 1 + return target.set( 1 - u - v, v, u ); } - convertGammaToLinear( gammaFactor ) { + static containsPoint( point, a, b, c ) { - this.copyGammaToLinear( this, gammaFactor ); + this.getBarycoord( point, a, b, c, _v3$1 ); - return this; + return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); } - convertLinearToGamma( gammaFactor ) { + static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { - this.copyLinearToGamma( this, gammaFactor ); + this.getBarycoord( point, p1, p2, p3, _v3$1 ); - return this; + target.set( 0, 0 ); + target.addScaledVector( uv1, _v3$1.x ); + target.addScaledVector( uv2, _v3$1.y ); + target.addScaledVector( uv3, _v3$1.z ); + + return target; } - copySRGBToLinear( color ) { + static isFrontFacing( a, b, c, direction ) { - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); + _v0$1.subVectors( c, b ); + _v1$3.subVectors( a, b ); - return this; + // strictly front facing + return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; } - copyLinearToSRGB( color ) { + set( a, b, c ) { - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); return this; } - convertSRGBToLinear() { + setFromPointsAndIndices( points, i0, i1, i2 ) { - this.copySRGBToLinear( this ); + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); return this; } - convertLinearToSRGB() { + setFromAttributeAndIndices( attribute, i0, i1, i2 ) { - this.copyLinearToSRGB( this ); + this.a.fromBufferAttribute( attribute, i0 ); + this.b.fromBufferAttribute( attribute, i1 ); + this.c.fromBufferAttribute( attribute, i2 ); return this; } - getHex() { + clone() { - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + return new this.constructor().copy( this ); } - getHexString() { + copy( triangle ) { + + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + return this; } - getHSL( target ) { + getArea() { - // h,s,l ranges are in 0.0 - 1.0 + _v0$1.subVectors( this.c, this.b ); + _v1$3.subVectors( this.a, this.b ); - if ( target === undefined ) { + return _v0$1.cross( _v1$3 ).length() * 0.5; - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; + } - } + getMidpoint( target ) { - const r = this.r, g = this.g, b = this.b; + return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - const max = Math.max( r, g, b ); - const min = Math.min( r, g, b ); - - let hue, saturation; - const lightness = ( min + max ) / 2.0; - - if ( min === max ) { + } - hue = 0; - saturation = 0; + getNormal( target ) { - } else { + return Triangle.getNormal( this.a, this.b, this.c, target ); - const delta = max - min; + } - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + getPlane( target ) { - switch ( max ) { + return target.setFromCoplanarPoints( this.a, this.b, this.c ); - 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; + } - } + getBarycoord( point, target ) { - hue /= 6; + return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); - } + } - target.h = hue; - target.s = saturation; - target.l = lightness; + getUV( point, uv1, uv2, uv3, target ) { - return target; + return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); } - getStyle() { + containsPoint( point ) { - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + return Triangle.containsPoint( point, this.a, this.b, this.c ); } - offsetHSL( h, s, l ) { + isFrontFacing( direction ) { - this.getHSL( _hslA ); + return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - _hslA.h += h; _hslA.s += s; _hslA.l += l; + } - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); + intersectsBox( box ) { - return this; + return box.intersectsTriangle( this ); } - add( color ) { + closestPointToPoint( p, target ) { - this.r += color.r; - this.g += color.g; - this.b += color.b; + const a = this.a, b = this.b, c = this.c; + let v, w; - return this; + // algorithm thanks to Real-Time Collision Detection by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., + // under the accompanying license; see chapter 5.1.5 for detailed explanation. + // basically, we're distinguishing which of the voronoi regions of the triangle + // the point lies in with the minimum amount of redundant computation. - } + _vab.subVectors( b, a ); + _vac.subVectors( c, a ); + _vap.subVectors( p, a ); + const d1 = _vab.dot( _vap ); + const d2 = _vac.dot( _vap ); + if ( d1 <= 0 && d2 <= 0 ) { - addColors( color1, color2 ) { + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy( a ); - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; + } - return this; + _vbp.subVectors( p, b ); + const d3 = _vab.dot( _vbp ); + const d4 = _vac.dot( _vbp ); + if ( d3 >= 0 && d4 <= d3 ) { - } + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy( b ); - addScalar( s ) { + } - this.r += s; - this.g += s; - this.b += s; + const vc = d1 * d4 - d3 * d2; + if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - return this; + v = d1 / ( d1 - d3 ); + // edge region of AB; barycentric coords (1-v, v, 0) + return target.copy( a ).addScaledVector( _vab, v ); - } + } - sub( color ) { + _vcp.subVectors( p, c ); + const d5 = _vab.dot( _vcp ); + const d6 = _vac.dot( _vcp ); + if ( d6 >= 0 && d5 <= d6 ) { - 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 ); + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy( c ); - return this; + } - } + const vb = d5 * d2 - d1 * d6; + if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - multiply( color ) { + w = d2 / ( d2 - d6 ); + // edge region of AC; barycentric coords (1-w, 0, w) + return target.copy( a ).addScaledVector( _vac, w ); - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; + } - return this; + const va = d3 * d6 - d5 * d4; + if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { - } + _vbc.subVectors( c, b ); + w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); + // edge region of BC; barycentric coords (0, 1-w, w) + return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC - multiplyScalar( s ) { + } - this.r *= s; - this.g *= s; - this.b *= s; + // face region + const denom = 1 / ( va + vb + vc ); + // u = va * denom + v = vb * denom; + w = vc * denom; - return this; + return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); } - lerp( color, alpha ) { - - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; + equals( triangle ) { - return this; + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } - lerpColors( color1, color2, alpha ) { +} - this.r = color1.r + ( color2.r - color1.r ) * alpha; - this.g = color1.g + ( color2.g - color1.g ) * alpha; - this.b = color1.b + ( color2.b - color1.b ) * alpha; +let materialId = 0; - return this; +class Material extends EventDispatcher$1 { - } + constructor() { - lerpHSL( color, alpha ) { + super(); - this.getHSL( _hslA ); - color.getHSL( _hslB ); + Object.defineProperty( this, 'id', { value: materialId ++ } ); - const h = lerp( _hslA.h, _hslB.h, alpha ); - const s = lerp( _hslA.s, _hslB.s, alpha ); - const l = lerp( _hslA.l, _hslB.l, alpha ); + this.uuid = generateUUID(); - this.setHSL( h, s, l ); + this.name = ''; + this.type = 'Material'; - return this; + this.fog = true; - } + this.blending = NormalBlending; + this.side = FrontSide; + this.vertexColors = false; - equals( c ) { + this.opacity = 1; + this.transparent = false; - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + 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; - fromArray( array, offset = 0 ) { + this.stencilWriteMask = 0xff; + this.stencilFunc = AlwaysStencilFunc; + this.stencilRef = 0; + this.stencilFuncMask = 0xff; + this.stencilFail = KeepStencilOp; + this.stencilZFail = KeepStencilOp; + this.stencilZPass = KeepStencilOp; + this.stencilWrite = false; - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; + this.clippingPlanes = null; + this.clipIntersection = false; + this.clipShadows = false; - return this; + this.shadowSide = null; - } + this.colorWrite = true; - toArray( array = [], offset = 0 ) { + this.precision = null; // override the renderer's default precision for this material - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; - return array; + this.dithering = false; - } + this.alphaToCoverage = false; + this.premultipliedAlpha = false; - fromBufferAttribute( attribute, index ) { + this.visible = true; - this.r = attribute.getX( index ); - this.g = attribute.getY( index ); - this.b = attribute.getZ( index ); + this.toneMapped = true; - if ( attribute.normalized === true ) { + this.userData = {}; - // assuming Uint8Array + this.version = 0; - this.r /= 255; - this.g /= 255; - this.b /= 255; + this._alphaTest = 0; - } + } - return this; + get alphaTest() { + + return this._alphaTest; } - toJSON() { + set alphaTest( value ) { - return this.getHex(); + if ( this._alphaTest > 0 !== value > 0 ) { - } + this.version ++; -} + } -Color.NAMES = _colorKeywords; + this._alphaTest = value; -Color.prototype.isColor = true; -Color.prototype.r = 1; -Color.prototype.g = 1; -Color.prototype.b = 1; + } -/** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ + onBuild( /* shaderobject, renderer */ ) {} -class MeshBasicMaterial extends Material { + onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} - constructor( parameters ) { + onBeforeCompile( /* shaderobject, renderer */ ) {} - super(); + customProgramCacheKey() { - this.type = 'MeshBasicMaterial'; + return this.onBeforeCompile.toString(); - this.color = new Color( 0xffffff ); // emissive + } - this.map = null; + setValues( values ) { - this.lightMap = null; - this.lightMapIntensity = 1.0; + if ( values === undefined ) return; - this.aoMap = null; - this.aoMapIntensity = 1.0; + for ( const key in values ) { - this.specularMap = null; + const newValue = values[ key ]; - this.alphaMap = null; + if ( newValue === undefined ) { - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); + continue; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; + // for backward compatibility if shading is set in the constructor + if ( key === 'shading' ) { - this.setValues( parameters ); + console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.flatShading = ( newValue === FlatShading ) ? true : false; + continue; - } + } - copy( source ) { + const currentValue = this[ key ]; - super.copy( source ); + if ( currentValue === undefined ) { - this.color.copy( source.color ); + console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' ); + continue; - this.map = source.map; + } - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( currentValue && currentValue.isColor ) { - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + currentValue.set( newValue ); - this.specularMap = source.specularMap; + } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - this.alphaMap = source.alphaMap; + currentValue.copy( newValue ); - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + } else { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + this[ key ] = newValue; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + } - return this; + } } -} + toJSON( meta ) { -MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); -const _vector$9 = new Vector3(); -const _vector2 = new Vector2(); + if ( isRootObject ) { -class BufferAttribute { + meta = { + textures: {}, + images: {} + }; - constructor( array, itemSize, normalized ) { + } - if ( Array.isArray( array ) ) { + const data = { + metadata: { + version: 4.5, + type: 'Material', + generator: 'Material.toJSON' + } + }; - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; - } + if ( this.name !== '' ) data.name = this.name; - this.name = ''; + if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; + if ( this.roughness !== undefined ) data.roughness = this.roughness; + if ( this.metalness !== undefined ) data.metalness = this.metalness; - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; + if ( this.sheen !== undefined ) data.sheen = this.sheen; + if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); + if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; + if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); + if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - this.version = 0; + if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); + if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; + if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.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; - this.onUploadCallback = function () {}; + if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - } + data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - set needsUpdate( value ) { + } - if ( value === true ) this.version ++; + if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - } + data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - setUsage( value ) { + } - this.usage = value; + if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - return this; + data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; + data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - } + } - copy( source ) { + if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; + if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; + if ( this.lightMap && this.lightMap.isTexture ) { - this.usage = source.usage; + data.lightMap = this.lightMap.toJSON( meta ).uuid; + data.lightMapIntensity = this.lightMapIntensity; - return this; + } - } + if ( this.aoMap && this.aoMap.isTexture ) { - copyAt( index1, attribute, index2 ) { + data.aoMap = this.aoMap.toJSON( meta ).uuid; + data.aoMapIntensity = this.aoMapIntensity; - index1 *= this.itemSize; - index2 *= attribute.itemSize; + } - for ( let i = 0, l = this.itemSize; i < l; i ++ ) { + if ( this.bumpMap && this.bumpMap.isTexture ) { - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; } - return this; + if ( this.normalMap && this.normalMap.isTexture ) { - } + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalMapType = this.normalMapType; + data.normalScale = this.normalScale.toArray(); - copyArray( array ) { + } - this.array.set( array ); + if ( this.displacementMap && this.displacementMap.isTexture ) { - return this; + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; - } + } - copyColorsArray( colors ) { + 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; - const array = this.array; - let offset = 0; + 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.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; + if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; - for ( let i = 0, l = colors.length; i < l; i ++ ) { + if ( this.envMap && this.envMap.isTexture ) { - let color = colors[ i ]; + data.envMap = this.envMap.toJSON( meta ).uuid; - if ( color === undefined ) { + if ( this.combine !== undefined ) data.combine = this.combine; - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); + } - } + if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; + if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; + if ( this.gradientMap && this.gradientMap.isTexture ) { + + data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } - return this; + if ( this.transmission !== undefined ) data.transmission = this.transmission; + if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; + if ( this.thickness !== undefined ) data.thickness = this.thickness; + if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; + if ( this.attenuationDistance !== undefined ) data.attenuationDistance = this.attenuationDistance; + if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); - } + if ( this.size !== undefined ) data.size = this.size; + if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - copyVector2sArray( vectors ) { + if ( this.blending !== NormalBlending ) data.blending = this.blending; + if ( this.side !== FrontSide ) data.side = this.side; + if ( this.vertexColors ) data.vertexColors = true; - const array = this.array; - let offset = 0; + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = this.transparent; - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + data.depthFunc = this.depthFunc; + data.depthTest = this.depthTest; + data.depthWrite = this.depthWrite; + data.colorWrite = this.colorWrite; - let vector = vectors[ i ]; + data.stencilWrite = this.stencilWrite; + data.stencilWriteMask = this.stencilWriteMask; + data.stencilFunc = this.stencilFunc; + data.stencilRef = this.stencilRef; + data.stencilFuncMask = this.stencilFuncMask; + data.stencilFail = this.stencilFail; + data.stencilZFail = this.stencilZFail; + data.stencilZPass = this.stencilZPass; - if ( vector === undefined ) { + // rotation (SpriteMaterial) + if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); + if ( this.polygonOffset === true ) data.polygonOffset = true; + if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; + if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - } + if ( this.linewidth !== undefined && 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; - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; + if ( this.dithering === true ) data.dithering = true; - } + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.alphaToCoverage === true ) data.alphaToCoverage = this.alphaToCoverage; + if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; - return this; + 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.flatShading === true ) data.flatShading = this.flatShading; - copyVector3sArray( vectors ) { + if ( this.visible === false ) data.visible = false; - const array = this.array; - let offset = 0; + if ( this.toneMapped === false ) data.toneMapped = false; - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; - let vector = vectors[ i ]; + // TODO: Copied from Object3D.toJSON - if ( vector === undefined ) { + function extractFromCache( cache ) { - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); + const values = []; + + for ( const key in cache ) { + + const data = cache[ key ]; + delete data.metadata; + values.push( data ); } - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; + return values; } - return this; + if ( isRootObject ) { - } + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); - copyVector4sArray( vectors ) { + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; - const array = this.array; - let offset = 0; + } - for ( let i = 0, l = vectors.length; i < l; i ++ ) { + return data; - let vector = vectors[ i ]; + } - if ( vector === undefined ) { + clone() { + + return new this.constructor().copy( this ); + + } + + copy( source ) { + + this.name = source.name; + + this.fog = source.fog; + + this.blending = source.blending; + this.side = source.side; + 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.stencilWriteMask = source.stencilWriteMask; + this.stencilFunc = source.stencilFunc; + this.stencilRef = source.stencilRef; + this.stencilFuncMask = source.stencilFuncMask; + this.stencilFail = source.stencilFail; + this.stencilZFail = source.stencilZFail; + this.stencilZPass = source.stencilZPass; + this.stencilWrite = source.stencilWrite; + + const srcPlanes = source.clippingPlanes; + let dstPlanes = null; + + if ( srcPlanes !== null ) { + + const n = srcPlanes.length; + dstPlanes = new Array( n ); + + for ( let i = 0; i !== n; ++ i ) { + + dstPlanes[ i ] = srcPlanes[ i ].clone(); + + } + + } + + this.clippingPlanes = dstPlanes; + this.clipIntersection = source.clipIntersection; + this.clipShadows = source.clipShadows; + + this.shadowSide = source.shadowSide; + + 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.alphaToCoverage = source.alphaToCoverage; + this.premultipliedAlpha = source.premultipliedAlpha; + + this.visible = source.visible; + + this.toneMapped = source.toneMapped; + + this.userData = JSON.parse( JSON.stringify( source.userData ) ); + + return this; + + } + + dispose() { + + this.dispatchEvent( { type: 'dispose' } ); + + } + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + +} + +Material.prototype.isMaterial = true; + +Material.fromType = function ( /*type*/ ) { + + // TODO: Behavior added in Materials.js + + return null; + +}; + +class MeshBasicMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.type = 'MeshBasicMaterial'; + + this.color = new Color( 0xffffff ); // emissive + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + return this; + + } + +} + +MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + +const _vector$9 = /*@__PURE__*/ new Vector3(); +const _vector2$1 = /*@__PURE__*/ new Vector2(); + +class BufferAttribute { + + constructor( array, itemSize, normalized ) { + + if ( Array.isArray( array ) ) { + + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + + } + + this.name = ''; + + this.array = array; + this.itemSize = itemSize; + this.count = array !== undefined ? array.length / itemSize : 0; + this.normalized = normalized === true; + + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + + } + + onUploadCallback() {} + + set needsUpdate( value ) { + + if ( value === true ) this.version ++; + + } + + setUsage( value ) { + + this.usage = value; + + return this; + + } + + copy( source ) { + + this.name = source.name; + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; + + this.usage = source.usage; + + return this; + + } + + copyAt( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( let i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + } + + copyArray( array ) { + + this.array.set( array ); + + return this; + + } + + copyColorsArray( colors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = colors.length; i < l; i ++ ) { + + let color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new Color(); + + } + + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + + } + + return this; + + } + + copyVector2sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new Vector2(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + + } + + return this; + + } + + copyVector3sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new Vector3(); + + } + + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + + } + + return this; + + } + + copyVector4sArray( vectors ) { + + const array = this.array; + let offset = 0; + + for ( let i = 0, l = vectors.length; i < l; i ++ ) { + + let vector = vectors[ i ]; + + if ( vector === undefined ) { console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); vector = new Vector4(); @@ -10706,10 +10889,10 @@ class BufferAttribute { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector2.fromBufferAttribute( this, i ); - _vector2.applyMatrix3( m ); + _vector2$1.fromBufferAttribute( this, i ); + _vector2$1.applyMatrix3( m ); - this.setXY( i, _vector2.x, _vector2.y ); + this.setXY( i, _vector2$1.x, _vector2$1.y ); } @@ -10734,9 +10917,7 @@ class BufferAttribute { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + _vector$9.fromBufferAttribute( this, i ); _vector$9.applyMatrix4( m ); @@ -10752,9 +10933,7 @@ class BufferAttribute { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + _vector$9.fromBufferAttribute( this, i ); _vector$9.applyNormalMatrix( m ); @@ -10770,9 +10949,7 @@ class BufferAttribute { for ( let i = 0, l = this.count; i < l; i ++ ) { - _vector$9.x = this.getX( i ); - _vector$9.y = this.getY( i ); - _vector$9.z = this.getZ( i ); + _vector$9.fromBufferAttribute( this, i ); _vector$9.transformDirection( m ); @@ -10961,30 +11138,14 @@ class Float32BufferAttribute extends BufferAttribute { } -function arrayMax( array ) { - - if ( array.length === 0 ) return - Infinity; - - let max = array[ 0 ]; +let _id$1 = 0; - for ( let i = 1, l = array.length; i < l; ++ i ) { - - if ( array[ i ] > max ) max = array[ i ]; - - } - - return max; - -} - -let _id = 0; - -const _m1 = new Matrix4(); -const _obj = new Object3D(); -const _offset = new Vector3(); -const _box$1 = new Box3(); -const _boxMorphTargets = new Box3(); -const _vector$8 = new Vector3(); +const _m1 = /*@__PURE__*/ new Matrix4(); +const _obj = /*@__PURE__*/ new Object3D(); +const _offset = /*@__PURE__*/ new Vector3(); +const _box$1 = /*@__PURE__*/ new Box3(); +const _boxMorphTargets = /*@__PURE__*/ new Box3(); +const _vector$8 = /*@__PURE__*/ new Vector3(); class BufferGeometry extends EventDispatcher$1 { @@ -10992,7 +11153,7 @@ class BufferGeometry extends EventDispatcher$1 { super(); - Object.defineProperty( this, 'id', { value: _id ++ } ); + Object.defineProperty( this, 'id', { value: _id$1 ++ } ); this.uuid = generateUUID(); @@ -11026,7 +11187,7 @@ class BufferGeometry extends EventDispatcher$1 { if ( Array.isArray( index ) ) { - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); + this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); } else { @@ -11141,6 +11302,16 @@ class BufferGeometry extends EventDispatcher$1 { } + applyQuaternion( q ) { + + _m1.makeRotationFromQuaternion( q ); + + this.applyMatrix4( _m1 ); + + return this; + + } + rotateX( angle ) { // rotate geometry around world x-axis @@ -11424,12 +11595,6 @@ class BufferGeometry extends EventDispatcher$1 { } - computeFaceNormals() { - - // backwards compatibility - - } - computeTangents() { const index = this.index; @@ -11455,13 +11620,13 @@ class BufferGeometry extends EventDispatcher$1 { const nVertices = positions.length / 3; - if ( attributes.tangent === undefined ) { + if ( this.hasAttribute( 'tangent' ) === false ) { this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); } - const tangents = attributes.tangent.array; + const tangents = this.getAttribute( 'tangent' ).array; const tan1 = [], tan2 = []; @@ -11764,7 +11929,15 @@ class BufferGeometry extends EventDispatcher$1 { for ( let i = 0, l = indices.length; i < l; i ++ ) { - index = indices[ i ] * itemSize; + if ( attribute.isInterleavedBufferAttribute ) { + + index = indices[ i ] * attribute.data.stride + attribute.offset; + + } else { + + index = indices[ i ] * itemSize; + + } for ( let j = 0; j < itemSize; j ++ ) { @@ -11959,31 +12132,7 @@ class BufferGeometry extends EventDispatcher$1 { clone() { - /* - // Handle primitives - - const parameters = this.parameters; - - if ( parameters !== undefined ) { - - const values = []; - - for ( const key in parameters ) { - - values.push( parameters[ key ] ); - - } - - const geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; - - } - return new this.constructor().copy( this ); - */ - - return new BufferGeometry().copy( this ); } @@ -12088,6 +12237,10 @@ class BufferGeometry extends EventDispatcher$1 { this.userData = source.userData; + // geometry generator parameters + + if ( source.parameters !== undefined ) this.parameters = Object.assign( {}, source.parameters ); + return this; } @@ -12263,7 +12416,7 @@ class Mesh extends Object3D { const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -12321,7 +12474,7 @@ class Mesh extends Object3D { const groupMaterial = material[ group.materialIndex ]; const start = Math.max( group.start, drawRange.start ); - const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); for ( let j = start, jl = end; j < jl; j += 3 ) { @@ -12420,7 +12573,7 @@ function checkBufferGeometryIntersection( object, material, raycaster, ray, posi const morphInfluences = object.morphTargetInfluences; - if ( material.morphTargets && morphPosition && morphInfluences ) { + if ( morphPosition && morphInfluences ) { _morphA.set( 0, 0, 0 ); _morphB.set( 0, 0, 0 ); @@ -12459,7 +12612,7 @@ function checkBufferGeometryIntersection( object, material, raycaster, ray, posi } - if ( object.isSkinnedMesh && material.skinning ) { + if ( object.isSkinnedMesh ) { object.boneTransform( a, _vA$1 ); object.boneTransform( b, _vB$1 ); @@ -12666,6 +12819,12 @@ class BoxGeometry extends BufferGeometry { } + static fromJSON( data ) { + + return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); + + } + } /** @@ -12737,25 +12896,6 @@ var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelVie var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; -/** - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - class ShaderMaterial extends Material { constructor( parameters ) { @@ -12779,10 +12919,6 @@ class ShaderMaterial extends Material { 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 @@ -12834,11 +12970,6 @@ class ShaderMaterial extends Material { this.lights = source.lights; this.clipping = source.clipping; - this.skinning = source.skinning; - - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - this.extensions = Object.assign( {}, source.extensions ); this.glslVersion = source.glslVersion; @@ -12973,13 +13104,6 @@ class Camera extends Object3D { getWorldDirection( target ) { - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - this.updateWorldMatrix( true, false ); const e = this.matrixWorld.elements; @@ -13308,9 +13432,14 @@ class CubeCamera extends Object3D { const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; - const currentXrEnabled = renderer.xr.enabled; const currentRenderTarget = renderer.getRenderTarget(); + const currentOutputEncoding = renderer.outputEncoding; + const currentToneMapping = renderer.toneMapping; + const currentXrEnabled = renderer.xr.enabled; + + renderer.outputEncoding = LinearEncoding; + renderer.toneMapping = NoToneMapping; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; @@ -13339,8 +13468,12 @@ class CubeCamera extends Object3D { renderer.setRenderTarget( currentRenderTarget ); + renderer.outputEncoding = currentOutputEncoding; + renderer.toneMapping = currentToneMapping; renderer.xr.enabled = currentXrEnabled; + renderTarget.texture.needsPMREMUpdate = true; + } } @@ -13351,22 +13484,9 @@ class CubeTexture extends Texture { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - // Why CubeTexture._needsFlipEnvMap is necessary: - // - // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) - // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, - // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - - // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped - // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false) - // when using WebGLCubeRenderTarget.texture as a cube texture. - - this._needsFlipEnvMap = true; - this.flipY = false; } @@ -13389,33 +13509,33 @@ CubeTexture.prototype.isCubeTexture = true; class WebGLCubeRenderTarget extends WebGLRenderTarget { - constructor( size, options, dummy ) { - - if ( Number.isInteger( options ) ) { + constructor( size, options = {} ) { - console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' ); + super( size, size, options ); - options = dummy; + const image = { width: size, height: size, depth: 1 }; + const images = [ image, image, image, image, image, image ]; - } + this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - super( size, size, options ); + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - options = options || {}; + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture + // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). - this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - this.texture._needsFlipEnvMap = false; - } fromEquirectangularTexture( renderer, texture ) { this.texture.type = texture.type; - this.texture.format = RGBAFormat; // see #18859 this.texture.encoding = texture.encoding; this.texture.generateMipmaps = texture.generateMipmaps; @@ -13523,28 +13643,206 @@ class WebGLCubeRenderTarget extends WebGLRenderTarget { WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true; -class DataTexture extends Texture { +const _vector1 = /*@__PURE__*/ new Vector3(); +const _vector2 = /*@__PURE__*/ new Vector3(); +const _normalMatrix = /*@__PURE__*/ new Matrix3(); - constructor( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { +class Plane { - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { - this.image = { data: data || null, width: width || 1, height: height || 1 }; + // normal is assumed to be normalized - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + this.normal = normal; + this.constant = constant; - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + } - this.needsUpdate = true; + set( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + } + + setComponents( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + } + + setFromNormalAndCoplanarPoint( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); + + return this; + + } + + setFromCoplanarPoints( a, b, c ) { + + const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + } + + copy( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + } + + normalize() { + + // Note: will lead to a divide by zero if the plane is invalid. + + const inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + } + + negate() { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + } + + distanceToPoint( point ) { + + return this.normal.dot( point ) + this.constant; + + } + + distanceToSphere( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + } + + projectPoint( point, target ) { + + return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + + } + + intersectLine( line, target ) { + + const direction = line.delta( _vector1 ); + + const denominator = this.normal.dot( direction ); + + if ( denominator === 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { + + return target.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return null; + + } + + const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return null; + + } + + return target.copy( direction ).multiplyScalar( t ).add( line.start ); + + } + + intersectsLine( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + const startSign = this.distanceToPoint( line.start ); + const endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + } + + intersectsBox( box ) { + + return box.intersectsPlane( this ); + + } + + intersectsSphere( sphere ) { + + return sphere.intersectsPlane( this ); + + } + + coplanarPoint( target ) { + + return target.copy( this.normal ).multiplyScalar( - this.constant ); + + } + + applyMatrix4( matrix, optionalNormalMatrix ) { + + const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); + + const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); + + const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + + this.constant = - referencePoint.dot( normal ); + + return this; + + } + + translate( offset ) { + + this.constant -= offset.dot( this.normal ); + + return this; + + } + + equals( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + + } + + clone() { + + return new this.constructor().copy( this ); } } -DataTexture.prototype.isDataTexture = true; +Plane.prototype.isPlane = true; const _sphere$2 = /*@__PURE__*/ new Sphere(); const _vector$7 = /*@__PURE__*/ new Vector3(); @@ -13772,16 +14070,12 @@ function WebGLAttributes( gl, capabilities ) { attribute.onUploadCallback(); - let type = 5126; + let type; if ( array instanceof Float32Array ) { type = 5126; - } else if ( array instanceof Float64Array ) { - - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); - } else if ( array instanceof Uint16Array ) { if ( attribute.isFloat16BufferAttribute ) { @@ -13792,7 +14086,7 @@ function WebGLAttributes( gl, capabilities ) { } else { - console.warn( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); + throw new Error( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); } @@ -13822,6 +14116,14 @@ function WebGLAttributes( gl, capabilities ) { type = 5121; + } else if ( array instanceof Uint8ClampedArray ) { + + type = 5121; + + } else { + + throw new Error( 'THREE.WebGLAttributes: Unsupported buffer data format: ' + array ); + } return { @@ -14016,15 +14318,23 @@ class PlaneGeometry extends BufferGeometry { } + static fromJSON( data ) { + + return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); + + } + } var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; -var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; +var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; -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( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; +var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; + +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( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; @@ -14032,7 +14342,7 @@ var begin_vertex = "vec3 transformed = vec3( position );"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; -var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\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\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\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}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\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 vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( 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 vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\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#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; +var bsdfs = "vec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat V_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_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 f0, const in float f90, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( V * 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}\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_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif"; 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, float faceDirection ) {\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 ) * faceDirection;\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"; @@ -14052,9 +14362,9 @@ var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\ var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; -var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(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}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\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#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\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}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; +var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( 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 max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\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}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\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#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\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}\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}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}"; -var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; +var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 ) + 0.5;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\t\t#ifdef texture2DGradEXT\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\n\t\t#else\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\t\t#endif\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, CUBEUV_MAX_MIP );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; @@ -14062,17 +14372,17 @@ var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif"; -var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; +var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; -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.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\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.a );\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.a );\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.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, 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.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.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 = cLogLuvM * value.rgb;\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 = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; +var encodings_pars_fragment = "vec4 LinearToLinear( in vec4 value ) {\n\treturn value;\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.a );\n}"; -var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, 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_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\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"; +var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, 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_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\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"; -var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; +var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; @@ -14080,41 +14390,41 @@ var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || def var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\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"; -var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif"; +var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; -var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; +var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; -var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\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"; +var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; -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"; +var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\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"; -var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; +var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn vec3( texture2D( gradientMap, coord ).r );\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}"; -var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif"; +var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; 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 = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; +var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry.normal );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry.normal );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointLightInfo( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotLightInfo( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalLightInfo( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = 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\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry.normal );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif"; -var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\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};\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};\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};\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"; +var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#else\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.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};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.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};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.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 vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; -var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\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\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 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 roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, 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\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; +var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n#endif"; var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; -var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; +var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)"; var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; -var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; +var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, 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_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)"; -var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; +var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\t#ifdef SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULARINTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vUv ).a;\n\t\t#endif\n\t\t#ifdef USE_SPECULARCOLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vUv ).rgb;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( ior - 1.0 ) / ( ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vUv ).a;\n\t#endif\n#endif"; -var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#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#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\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 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\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\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}"; +var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec3 sheenSpecular = vec3( 0.0 );\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\treturn saturate( DG * RECIPROCAL_PI );\n}\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, 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 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\tvec3 FssEss = specularColor * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\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.roughness;\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#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularF90, material.roughness );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\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_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tcomputeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\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\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}"; -var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; +var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef USE_CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\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\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry.normal );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; -var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * 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 defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; +var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometry.normal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; @@ -14126,11 +14436,11 @@ var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_ var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; -var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; +var map_fragment = "#ifdef USE_MAP\n\tvec4 sampledDiffuseColor = texture2D( map, vUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\tsampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );\n\t#endif\n\tdiffuseColor *= sampledDiffuseColor;\n#endif"; var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; -var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; +var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tdiffuseColor *= texture2D( map, uv );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; @@ -14138,25 +14448,35 @@ var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_META var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; -var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif"; +var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\n\tvColor *= morphTargetBaseInfluence;\n\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t#if defined( USE_COLOR_ALPHA )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\n\t\t#elif defined( USE_COLOR )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\n\t\t#endif\n\t}\n#endif"; + +var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\t#endif\n#endif"; -var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; +var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform ivec2 morphTargetsTextureSize;\n\t\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\n\t\t\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\n\t\t\tint y = texelIndex / morphTargetsTextureSize.x;\n\t\t\tint x = texelIndex - y * morphTargetsTextureSize.x;\n\t\t\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\n\t\t\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\n\t\t}\n\t#else\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\t\t#else\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\t\t#endif\n\t#endif\n#endif"; -var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; +var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t\t#endif\n\t#endif\n#endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#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 * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; -var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; +var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; + +var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; + +var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; + +var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\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 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif"; -var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; +var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; -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}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\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}"; +var output_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= transmissionAlpha + 0.1;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; + +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}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\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}"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; @@ -14192,11 +14512,11 @@ var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D spe var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; -var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\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}\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}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; +var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\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}\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}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; -var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif"; +var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tfloat transmissionAlpha = 1.0;\n\tfloat transmissionFactor = transmission;\n\tfloat thicknessFactor = thickness;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\ttransmissionFactor *= texture2D( transmissionMap, vUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tthicknessFactor *= texture2D( thicknessMap, vUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmission = getIBLVolumeRefraction(\n\t\tn, v, roughnessFactor, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, ior, thicknessFactor,\n\t\tattenuationColor, attenuationDistance );\n\ttotalDiffuse = mix( totalDiffuse, transmission.rgb, transmissionFactor );\n\ttransmissionAlpha = mix( transmissionAlpha, transmission.a, transmissionFactor );\n#endif"; -var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif"; +var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\t\tfloat framebufferLod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\t#ifdef texture2DLodEXT\n\t\t\treturn texture2DLodEXT( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#else\n\t\t\treturn texture2D( transmissionSamplerMap, fragCoord.xy, framebufferLod );\n\t\t#endif\n\t}\n\tvec3 applyVolumeAttenuation( const in vec3 radiance, const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tif ( attenuationDistance == 0.0 ) {\n\t\t\treturn radiance;\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance * radiance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 attenuatedColor = applyVolumeAttenuation( transmittedLight.rgb, length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor * diffuseColor, transmittedLight.a );\n\t}\n#endif"; var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif"; @@ -14210,76 +14530,77 @@ var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tat var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif"; -var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; +var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; -var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; +const vertex$g = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; -var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; +const fragment$g = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tgl_FragColor = texture2D( t2D, vUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\tgl_FragColor = vec4( mix( pow( gl_FragColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), gl_FragColor.rgb * 0.0773993808, vec3( lessThanEqual( gl_FragColor.rgb, vec3( 0.04045 ) ) ) ), gl_FragColor.w );\n\t#endif\n\t#include \n\t#include \n}"; -var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; +const vertex$f = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; -var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; +const fragment$f = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; -var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; +const vertex$e = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; -var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; +const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; -var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; +const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; -var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; +const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; -var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; +const vertex$c = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; -var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; +const fragment$c = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\t#include \n\t#include \n}"; -var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$b = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$b = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$a = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \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 \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$9 = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$9 = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_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_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; -var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; +const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t#else\n\t\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshtoon_frag = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; -var meshtoon_vert = "#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; +const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; -var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; -var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; +const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULARINTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n\t#ifdef USE_SPECULARCOLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEENCOLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEENROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; +const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; -var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; +const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \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 \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const vertex$3 = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; +const vertex$2 = "#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; -var shadow_vert = "#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$2 = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; -var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; +const vertex$1 = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; -var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; +const fragment$1 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; const ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, + alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, @@ -14337,15 +14658,20 @@ const ShaderChunk = { map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, + morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, + normal_pars_fragment: normal_pars_fragment, + normal_pars_vertex: normal_pars_vertex, + normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, + output_fragment: output_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, @@ -14365,8 +14691,8 @@ const ShaderChunk = { specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, - transmissionmap_fragment: transmissionmap_fragment, - transmissionmap_pars_fragment: transmissionmap_pars_fragment, + transmission_fragment: transmission_fragment, + transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, @@ -14375,38 +14701,38 @@ const ShaderChunk = { uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, - background_frag: background_frag, - background_vert: background_vert, - 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, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshtoon_frag: meshtoon_frag, - meshtoon_vert: meshtoon_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, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert + background_vert: vertex$g, + background_frag: fragment$g, + cube_vert: vertex$f, + cube_frag: fragment$f, + depth_vert: vertex$e, + depth_frag: fragment$e, + distanceRGBA_vert: vertex$d, + distanceRGBA_frag: fragment$d, + equirect_vert: vertex$c, + equirect_frag: fragment$c, + linedashed_vert: vertex$b, + linedashed_frag: fragment$b, + meshbasic_vert: vertex$a, + meshbasic_frag: fragment$a, + meshlambert_vert: vertex$9, + meshlambert_frag: fragment$9, + meshmatcap_vert: vertex$8, + meshmatcap_frag: fragment$8, + meshnormal_vert: vertex$7, + meshnormal_frag: fragment$7, + meshphong_vert: vertex$6, + meshphong_frag: fragment$6, + meshphysical_vert: vertex$5, + meshphysical_frag: fragment$5, + meshtoon_vert: vertex$4, + meshtoon_frag: fragment$4, + points_vert: vertex$3, + points_frag: fragment$3, + shadow_vert: vertex$2, + shadow_frag: fragment$2, + sprite_vert: vertex$1, + sprite_frag: fragment$1 }; /** @@ -14417,7 +14743,7 @@ const UniformsLib = { common: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, map: { value: null }, @@ -14425,6 +14751,7 @@ const UniformsLib = { uv2Transform: { value: new Matrix3() }, alphaMap: { value: null }, + alphaTest: { value: 0 } }, @@ -14438,9 +14765,9 @@ const UniformsLib = { envMap: { value: null }, flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } + reflectivity: { value: 1.0 }, // basic, lambert, phong + ior: { value: 1.5 }, // physical + refractionRatio: { value: 0.98 } // basic, lambert, phong }, @@ -14594,24 +14921,26 @@ const UniformsLib = { points: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } }, sprite: { - diffuse: { value: new Color( 0xeeeeee ) }, + diffuse: { value: new Color( 0xffffff ) }, opacity: { value: 1.0 }, center: { value: new Vector2( 0.5, 0.5 ) }, rotation: { value: 0.0 }, map: { value: null }, alphaMap: { value: null }, + alphaTest: { value: 0 }, uvTransform: { value: new Matrix3() } } @@ -14805,8 +15134,8 @@ const ShaderLib = { } ] ), - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag + vertexShader: ShaderChunk.meshnormal_vert, + fragmentShader: ShaderChunk.meshnormal_frag }, @@ -14908,9 +15237,23 @@ ShaderLib.physical = { clearcoatRoughnessMap: { value: null }, clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, clearcoatNormalMap: { value: null }, - sheen: { value: new Color( 0x000000 ) }, + sheen: { value: 0 }, + sheenColor: { value: new Color( 0x000000 ) }, + sheenColorMap: { value: null }, + sheenRoughness: { value: 1 }, + sheenRoughnessMap: { value: null }, transmission: { value: 0 }, transmissionMap: { value: null }, + transmissionSamplerSize: { value: new Vector2() }, + transmissionSamplerMap: { value: null }, + thickness: { value: 0 }, + thicknessMap: { value: null }, + attenuationDistance: { value: 0 }, + attenuationColor: { value: new Color( 0x000000 ) }, + specularIntensity: { value: 1 }, + specularIntensityMap: { value: null }, + specularColor: { value: new Color( 1, 1, 1 ) }, + specularColorMap: { value: null }, } ] ), @@ -14919,10 +15262,10 @@ ShaderLib.physical = { }; -function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) { +function WebGLBackground( renderer, cubemaps, state, objects, alpha, premultipliedAlpha ) { const clearColor = new Color( 0x000000 ); - let clearAlpha = 0; + let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; @@ -14931,8 +15274,9 @@ function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha let currentBackgroundVersion = 0; let currentTonemapping = null; - function render( renderList, scene, camera, forceClear ) { + function render( renderList, scene ) { + let forceClear = false; let background = scene.isScene === true ? scene.background : null; if ( background && background.isTexture ) { @@ -15013,7 +15357,7 @@ function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha } boxMesh.material.uniforms.envMap.value = background; - boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background._needsFlipEnvMap ) ? - 1 : 1; + boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; if ( currentBackground !== background || currentBackgroundVersion !== background.version || @@ -15143,6 +15487,7 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const defaultState = createBindingState( null ); let currentState = defaultState; + let forceUpdate = false; function setup( object, material, program, geometry, index ) { @@ -15193,7 +15538,9 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { } - if ( updateBuffers ) { + if ( updateBuffers || forceUpdate ) { + + forceUpdate = false; setupVertexAttributes( object, material, program, geometry ); @@ -15456,9 +15803,16 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const programAttribute = programAttributes[ name ]; - if ( programAttribute >= 0 ) { + if ( programAttribute.location >= 0 ) { + + let geometryAttribute = geometryAttributes[ name ]; - const geometryAttribute = geometryAttributes[ name ]; + if ( geometryAttribute === undefined ) { + + if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; + + } if ( geometryAttribute !== undefined ) { @@ -15481,87 +15835,87 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { const stride = data.stride; const offset = geometryAttribute.offset; - if ( data && data.isInstancedInterleavedBuffer ) { - - enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); + if ( data.isInstancedInterleavedBuffer ) { - if ( geometry._maxInstanceCount === undefined ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - geometry._maxInstanceCount = data.meshPerAttribute * data.count; + enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); } - } else { - - enableAttribute( programAttribute ); + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - } + geometry._maxInstanceCount = data.meshPerAttribute * data.count; - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); + } - } else { + } else { - if ( geometryAttribute.isInstancedBufferAttribute ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); + enableAttribute( programAttribute.location + i ); - if ( geometry._maxInstanceCount === undefined ) { + } - geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + } - } + gl.bindBuffer( 34962, buffer ); - } else { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttribute( programAttribute ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + stride * bytesPerElement, + ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement + ); } - gl.bindBuffer( 34962, buffer ); - vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); + } else { - } + if ( geometryAttribute.isInstancedBufferAttribute ) { - } else if ( name === 'instanceMatrix' ) { + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - const attribute = attributes.get( object.instanceMatrix ); + enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - const buffer = attribute.buffer; - const type = attribute.type; + geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - enableAttributeAndDivisor( programAttribute + 0, 1 ); - enableAttributeAndDivisor( programAttribute + 1, 1 ); - enableAttributeAndDivisor( programAttribute + 2, 1 ); - enableAttributeAndDivisor( programAttribute + 3, 1 ); + } - gl.bindBuffer( 34962, buffer ); + } else { - gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 ); - gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 ); - gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 ); - gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 ); + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - } else if ( name === 'instanceColor' ) { + enableAttribute( programAttribute.location + i ); - const attribute = attributes.get( object.instanceColor ); + } - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + gl.bindBuffer( 34962, buffer ); - const buffer = attribute.buffer; - const type = attribute.type; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - enableAttributeAndDivisor( programAttribute, 1 ); + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + size * bytesPerElement, + ( size / programAttribute.locationSize ) * i * bytesPerElement + ); - gl.bindBuffer( 34962, buffer ); + } - gl.vertexAttribPointer( programAttribute, 3, type, false, 12, 0 ); + } } else if ( materialDefaultAttributeValues !== undefined ) { @@ -15572,19 +15926,19 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { switch ( value.length ) { case 2: - gl.vertexAttrib2fv( programAttribute, value ); + gl.vertexAttrib2fv( programAttribute.location, value ); break; case 3: - gl.vertexAttrib3fv( programAttribute, value ); + gl.vertexAttrib3fv( programAttribute.location, value ); break; case 4: - gl.vertexAttrib4fv( programAttribute, value ); + gl.vertexAttrib4fv( programAttribute.location, value ); break; default: - gl.vertexAttrib1fv( programAttribute, value ); + gl.vertexAttrib1fv( programAttribute.location, value ); } @@ -15683,6 +16037,7 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { function reset() { resetDefaultState(); + forceUpdate = true; if ( currentState === defaultState ) return; @@ -15691,7 +16046,7 @@ function WebGLBindingStates( gl, extensions, attributes, capabilities ) { } - // for backward-compatilibity + // for backward-compatibility function resetDefaultState() { @@ -15831,10 +16186,8 @@ function WebGLCapabilities( gl, extensions, parameters ) { } - /* eslint-disable no-undef */ const isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext ) || ( typeof WebGL2ComputeRenderingContext !== 'undefined' && gl instanceof WebGL2ComputeRenderingContext ); - /* eslint-enable no-undef */ let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; const maxPrecision = getMaxPrecision( precision ); @@ -15846,6 +16199,8 @@ function WebGLCapabilities( gl, extensions, parameters ) { } + const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ); + const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter( 34930 ); @@ -15868,6 +16223,8 @@ function WebGLCapabilities( gl, extensions, parameters ) { isWebGL2: isWebGL2, + drawBuffers: drawBuffers, + getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, @@ -16078,7 +16435,7 @@ function WebGLCubeMaps( renderer ) { function get( texture ) { - if ( texture && texture.isTexture ) { + if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { const mapping = texture.mapping; @@ -16095,14 +16452,10 @@ function WebGLCubeMaps( renderer ) { if ( image && image.height > 0 ) { - const currentRenderTarget = renderer.getRenderTarget(); - const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); renderTarget.fromEquirectangularTexture( renderer, texture ); cubemaps.set( texture, renderTarget ); - renderer.setRenderTarget( currentRenderTarget ); - texture.addEventListener( 'dispose', onTextureDispose ); return mapTextureMapping( renderTarget.texture, texture.mapping ); @@ -16155,14941 +16508,14684 @@ function WebGLCubeMaps( renderer ) { } -function WebGLExtensions( gl ) { +class OrthographicCamera extends Camera { - const extensions = {}; + constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { - function getExtension( name ) { + super(); - if ( extensions[ name ] !== undefined ) { + this.type = 'OrthographicCamera'; - return extensions[ name ]; + this.zoom = 1; + this.view = null; - } + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - let extension; + this.near = near; + this.far = far; - switch ( name ) { + this.updateProjectionMatrix(); - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; + } - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; + copy( source, recursive ) { - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; + super.copy( source, recursive ); - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - default: - extension = gl.getExtension( name ); + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); + + return this; + + } + + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { + + if ( this.view === null ) { + + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; } - extensions[ name ] = extension; + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; - return extension; + this.updateProjectionMatrix(); } - return { + clearViewOffset() { - has: function ( name ) { + if ( this.view !== null ) { - return getExtension( name ) !== null; + this.view.enabled = false; - }, + } - init: function ( capabilities ) { + this.updateProjectionMatrix(); - if ( capabilities.isWebGL2 ) { + } - getExtension( 'EXT_color_buffer_float' ); + updateProjectionMatrix() { - } else { + const dx = ( this.right - this.left ) / ( 2 * this.zoom ); + const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + const cx = ( this.right + this.left ) / 2; + const cy = ( this.top + this.bottom ) / 2; - getExtension( 'WEBGL_depth_texture' ); - getExtension( 'OES_texture_float' ); - getExtension( 'OES_texture_half_float' ); - getExtension( 'OES_texture_half_float_linear' ); - getExtension( 'OES_standard_derivatives' ); - getExtension( 'OES_element_index_uint' ); - getExtension( 'OES_vertex_array_object' ); - getExtension( 'ANGLE_instanced_arrays' ); + let left = cx - dx; + let right = cx + dx; + let top = cy + dy; + let bottom = cy - dy; - } + if ( this.view !== null && this.view.enabled ) { - getExtension( 'OES_texture_float_linear' ); - getExtension( 'EXT_color_buffer_half_float' ); + const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; + const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - }, + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; - get: function ( name ) { + } - const extension = getExtension( name ); + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - if ( extension === null ) { + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + } - } + toJSON( meta ) { - return extension; + const data = super.toJSON( meta ); - } + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; - }; + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); -} + return data; -function WebGLGeometries( gl, attributes, info, bindingStates ) { + } - const geometries = {}; - const wireframeAttributes = new WeakMap(); +} - function onGeometryDispose( event ) { +OrthographicCamera.prototype.isOrthographicCamera = true; - const geometry = event.target; +const LOD_MIN = 4; + +// The standard deviations (radians) associated with the extra mips. These are +// chosen to approximate a Trowbridge-Reitz distribution function times the +// geometric shadowing function. These sigma values squared must match the +// variance #defines in cube_uv_reflection_fragment.glsl.js. +const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; + +// The maximum length of the blur for loop. Smaller sigmas will use fewer +// samples and exit early, but not recompile the shader. +const MAX_SAMPLES = 20; + +const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); +const _clearColor = /*@__PURE__*/ new Color(); +let _oldTarget = null; + +// Golden Ratio +const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; +const INV_PHI = 1 / PHI; + +// Vertices of a dodecahedron (except the opposites, which represent the +// same axis), used as axis directions evenly spread on a sphere. +const _axisDirections = [ + /*@__PURE__*/ new Vector3( 1, 1, 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, 1 ), + /*@__PURE__*/ new Vector3( 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), + /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), + /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), + /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; - if ( geometry.index !== null ) { +/** + * This class generates a Prefiltered, Mipmapped Radiance Environment Map + * (PMREM) from a cubeMap environment texture. This allows different levels of + * blur to be quickly accessed based on material roughness. It is packed into a + * special CubeUV format that allows us to perform custom interpolation so that + * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap + * chain, it only goes down to the LOD_MIN level (above), and then creates extra + * even more filtered 'mips' at the same LOD_MIN resolution, associated with + * higher roughness levels. In this way we maintain resolution to smoothly + * interpolate diffuse lighting while limiting sampling computation. + * + * Paper: Fast, Accurate Image-Based Lighting + * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view +*/ - attributes.remove( geometry.index ); +class PMREMGenerator { - } + constructor( renderer ) { - for ( const name in geometry.attributes ) { + this._renderer = renderer; + this._pingPongRenderTarget = null; - attributes.remove( geometry.attributes[ name ] ); + this._lodMax = 0; + this._cubeSize = 0; + this._lodPlanes = []; + this._sizeLods = []; + this._sigmas = []; - } + this._blurMaterial = null; + this._cubemapMaterial = null; + this._equirectMaterial = null; - geometry.removeEventListener( 'dispose', onGeometryDispose ); + this._compileMaterial( this._blurMaterial ); - delete geometries[ geometry.id ]; + } - const attribute = wireframeAttributes.get( geometry ); + /** + * Generates a PMREM from a supplied Scene, which can be faster than using an + * image if networking bandwidth is low. Optional sigma specifies a blur radius + * in radians to be applied to the scene before PMREM generation. Optional near + * and far planes ensure the scene is rendered in its entirety (the cubeCamera + * is placed at the origin). + */ + fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { - if ( attribute ) { + _oldTarget = this._renderer.getRenderTarget(); - attributes.remove( attribute ); - wireframeAttributes.delete( geometry ); + this._setSize( 256 ); - } + const cubeUVRenderTarget = this._allocateTargets(); + cubeUVRenderTarget.depthBuffer = true; - bindingStates.releaseStatesOfGeometry( geometry ); + this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); - if ( geometry.isInstancedBufferGeometry === true ) { + if ( sigma > 0 ) { - delete geometry._maxInstanceCount; + this._blur( cubeUVRenderTarget, 0, 0, sigma ); } - // + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - info.memory.geometries --; + return cubeUVRenderTarget; } - function get( object, geometry ) { - - if ( geometries[ geometry.id ] === true ) return geometry; + /** + * Generates a PMREM from an equirectangular texture, which can be either LDR + * or HDR. The ideal input image size is 1k (1024 x 512), + * as this matches best with the 256 x 256 cubemap output. + */ + fromEquirectangular( equirectangular, renderTarget = null ) { - geometry.addEventListener( 'dispose', onGeometryDispose ); + return this._fromTexture( equirectangular, renderTarget ); - geometries[ geometry.id ] = true; + } - info.memory.geometries ++; + /** + * Generates a PMREM from an cubemap texture, which can be either LDR + * or HDR. The ideal input cube size is 256 x 256, + * as this matches best with the 256 x 256 cubemap output. + */ + fromCubemap( cubemap, renderTarget = null ) { - return geometry; + return this._fromTexture( cubemap, renderTarget ); } - function update( geometry ) { - - const geometryAttributes = geometry.attributes; - - // Updating index buffer in VAO now. See WebGLBindingStates. + /** + * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileCubemapShader() { - for ( const name in geometryAttributes ) { + if ( this._cubemapMaterial === null ) { - attributes.update( geometryAttributes[ name ], 34962 ); + this._cubemapMaterial = _getCubemapMaterial(); + this._compileMaterial( this._cubemapMaterial ); } - // morph targets + } - const morphAttributes = geometry.morphAttributes; + /** + * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during + * your texture's network fetch for increased concurrency. + */ + compileEquirectangularShader() { - for ( const name in morphAttributes ) { + if ( this._equirectMaterial === null ) { - const array = morphAttributes[ name ]; + this._equirectMaterial = _getEquirectMaterial(); + this._compileMaterial( this._equirectMaterial ); - for ( let i = 0, l = array.length; i < l; i ++ ) { + } - attributes.update( array[ i ], 34962 ); + } - } + /** + * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, + * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on + * one of them will cause any others to also become unusable. + */ + dispose() { - } + this._dispose(); + + if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); + if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); } - function updateWireframeAttribute( geometry ) { + // private interface - const indices = []; + _setSize( cubeSize ) { - const geometryIndex = geometry.index; - const geometryPosition = geometry.attributes.position; - let version = 0; + this._lodMax = Math.floor( Math.log2( cubeSize ) ); + this._cubeSize = Math.pow( 2, this._lodMax ); - if ( geometryIndex !== null ) { + } - const array = geometryIndex.array; - version = geometryIndex.version; + _dispose() { - for ( let i = 0, l = array.length; i < l; i += 3 ) { + if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); - const a = array[ i + 0 ]; - const b = array[ i + 1 ]; - const c = array[ i + 2 ]; + if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); - indices.push( a, b, b, c, c, a ); + for ( let i = 0; i < this._lodPlanes.length; i ++ ) { - } + this._lodPlanes[ i ].dispose(); - } else { + } - const array = geometryPosition.array; - version = geometryPosition.version; + } - for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + _cleanup( outputTarget ) { - const a = i + 0; - const b = i + 1; - const c = i + 2; + this._renderer.setRenderTarget( _oldTarget ); + outputTarget.scissorTest = false; + _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); - indices.push( a, b, b, c, c, a ); + } - } + _fromTexture( texture, renderTarget ) { - } + if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { - const attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; + this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); - // Updating index buffer in VAO now. See WebGLBindingStates + } else { // Equirectangular - // + this._setSize( texture.image.width / 4 ); - const previousAttribute = wireframeAttributes.get( geometry ); + } - if ( previousAttribute ) attributes.remove( previousAttribute ); + _oldTarget = this._renderer.getRenderTarget(); - // + const cubeUVRenderTarget = renderTarget || this._allocateTargets(); + this._textureToCubeUV( texture, cubeUVRenderTarget ); + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - wireframeAttributes.set( geometry, attribute ); + return cubeUVRenderTarget; } - function getWireframeAttribute( geometry ) { - - const currentAttribute = wireframeAttributes.get( geometry ); - - if ( currentAttribute ) { + _allocateTargets() { - const geometryIndex = geometry.index; + const width = 3 * Math.max( this._cubeSize, 16 * 7 ); + const height = 4 * this._cubeSize - 32; - if ( geometryIndex !== null ) { + const params = { + magFilter: LinearFilter, + minFilter: LinearFilter, + generateMipmaps: false, + type: HalfFloatType, + format: RGBAFormat, + encoding: LinearEncoding, + depthBuffer: false + }; - // if the attribute is obsolete, create a new one + const cubeUVRenderTarget = _createRenderTarget( width, height, params ); - if ( currentAttribute.version < geometryIndex.version ) { + if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width ) { - updateWireframeAttribute( geometry ); + if ( this._pingPongRenderTarget !== null ) { - } + this._dispose(); } - } else { + this._pingPongRenderTarget = _createRenderTarget( width, height, params ); - updateWireframeAttribute( geometry ); + const { _lodMax } = this; + ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); + + this._blurMaterial = _getBlurShader( _lodMax, width, height ); } - return wireframeAttributes.get( geometry ); + return cubeUVRenderTarget; } - return { + _compileMaterial( material ) { - get: get, - update: update, + const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); + this._renderer.compile( tmpMesh, _flatCamera ); - getWireframeAttribute: getWireframeAttribute + } - }; + _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { -} + const fov = 90; + const aspect = 1; + const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); + const upSign = [ 1, - 1, 1, 1, 1, 1 ]; + const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; + const renderer = this._renderer; -function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { + const originalAutoClear = renderer.autoClear; + const toneMapping = renderer.toneMapping; + renderer.getClearColor( _clearColor ); - const isWebGL2 = capabilities.isWebGL2; + renderer.toneMapping = NoToneMapping; + renderer.autoClear = false; - let mode; + const backgroundMaterial = new MeshBasicMaterial( { + name: 'PMREM.Background', + side: BackSide, + depthWrite: false, + depthTest: false, + } ); - function setMode( value ) { + const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); - mode = value; + let useSolidColor = false; + const background = scene.background; - } + if ( background ) { - let type, bytesPerElement; + if ( background.isColor ) { - function setIndex( value ) { + backgroundMaterial.color.copy( background ); + scene.background = null; + useSolidColor = true; - type = value.type; - bytesPerElement = value.bytesPerElement; + } - } + } else { - function render( start, count ) { + backgroundMaterial.color.copy( _clearColor ); + useSolidColor = true; - gl.drawElements( mode, count, type, start * bytesPerElement ); + } - info.update( count, mode, 1 ); + for ( let i = 0; i < 6; i ++ ) { - } + const col = i % 3; - function renderInstances( start, count, primcount ) { + if ( col === 0 ) { - if ( primcount === 0 ) return; + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - let extension, methodName; + } else if ( col === 1 ) { - if ( isWebGL2 ) { + cubeCamera.up.set( 0, 0, upSign[ i ] ); + cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - extension = gl; - methodName = 'drawElementsInstanced'; + } else { - } else { + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; + } - if ( extension === null ) { + const size = this._cubeSize; - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); + + renderer.setRenderTarget( cubeUVRenderTarget ); + + if ( useSolidColor ) { + + renderer.render( backgroundBox, cubeCamera ); } + renderer.render( scene, cubeCamera ); + } - extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); + backgroundBox.geometry.dispose(); + backgroundBox.material.dispose(); - info.update( count, mode, primcount ); + renderer.toneMapping = toneMapping; + renderer.autoClear = originalAutoClear; + scene.background = background; } - // + _textureToCubeUV( texture, cubeUVRenderTarget ) { - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; + const renderer = this._renderer; -} + const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); -function WebGLInfo( gl ) { + if ( isCubeTexture ) { - const memory = { - geometries: 0, - textures: 0 - }; + if ( this._cubemapMaterial === null ) { - const render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; + this._cubemapMaterial = _getCubemapMaterial(); - function update( count, mode, instanceCount ) { + } - render.calls ++; + this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; - switch ( mode ) { + } else { - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; + if ( this._equirectMaterial === null ) { - case 1: - render.lines += instanceCount * ( count / 2 ); - break; + this._equirectMaterial = _getEquirectMaterial(); - case 3: - render.lines += instanceCount * ( count - 1 ); - break; + } - case 2: - render.lines += instanceCount * count; - break; + } - case 0: - render.points += instanceCount * count; - break; + const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; + const mesh = new Mesh( this._lodPlanes[ 0 ], material ); - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; + const uniforms = material.uniforms; - } + uniforms[ 'envMap' ].value = texture; - } + const size = this._cubeSize; - function reset() { + _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; + renderer.setRenderTarget( cubeUVRenderTarget ); + renderer.render( mesh, _flatCamera ); } - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; + _applyPMREM( cubeUVRenderTarget ) { -} + const renderer = this._renderer; + const autoClear = renderer.autoClear; + renderer.autoClear = false; -function numericalSort( a, b ) { + for ( let i = 1; i < this._lodPlanes.length; i ++ ) { - return a[ 0 ] - b[ 0 ]; + const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); -} + const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; -function absNumericalSort( a, b ) { + this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); + } -} + renderer.autoClear = autoClear; -function WebGLMorphtargets( gl ) { + } - const influencesList = {}; - const morphInfluences = new Float32Array( 8 ); + /** + * This is a two-pass Gaussian blur for a cubemap. Normally this is done + * vertically and horizontally, but this breaks down on a cube. Here we apply + * the blur latitudinally (around the poles), and then longitudinally (towards + * the poles) to approximate the orthogonally-separable blur. It is least + * accurate at the poles, but still does a decent job. + */ + _blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { - const workInfluences = []; + const pingPongRenderTarget = this._pingPongRenderTarget; - for ( let i = 0; i < 8; i ++ ) { + this._halfBlur( + cubeUVRenderTarget, + pingPongRenderTarget, + lodIn, + lodOut, + sigma, + 'latitudinal', + poleAxis ); - workInfluences[ i ] = [ i, 0 ]; + this._halfBlur( + pingPongRenderTarget, + cubeUVRenderTarget, + lodOut, + lodOut, + sigma, + 'longitudinal', + poleAxis ); } - function update( object, geometry, material, program ) { + _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - const objectInfluences = object.morphTargetInfluences; + const renderer = this._renderer; + const blurMaterial = this._blurMaterial; - // When object doesn't have morph target influences defined, we treat it as a 0-length array - // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences + if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - const length = objectInfluences === undefined ? 0 : objectInfluences.length; + console.error( + 'blur direction must be either latitudinal or longitudinal!' ); - let influences = influencesList[ geometry.id ]; + } - if ( influences === undefined ) { + // Number of standard deviations at which to cut off the discrete approximation. + const STANDARD_DEVIATIONS = 3; - // initialise list + const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); + const blurUniforms = blurMaterial.uniforms; - influences = []; + const pixels = this._sizeLods[ lodIn ] - 1; + const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); + const sigmaPixels = sigmaRadians / radiansPerPixel; + const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; - for ( let i = 0; i < length; i ++ ) { + if ( samples > MAX_SAMPLES ) { - influences[ i ] = [ i, 0 ]; + console.warn( `sigmaRadians, ${ + sigmaRadians}, is too large and will clip, as it requested ${ + samples} samples when the maximum is set to ${MAX_SAMPLES}` ); - } + } - influencesList[ geometry.id ] = influences; + const weights = []; + let sum = 0; - } + for ( let i = 0; i < MAX_SAMPLES; ++ i ) { - // Collect influences + const x = i / sigmaPixels; + const weight = Math.exp( - x * x / 2 ); + weights.push( weight ); - for ( let i = 0; i < length; i ++ ) { + if ( i === 0 ) { - const influence = influences[ i ]; + sum += weight; - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; + } else if ( i < samples ) { - } + sum += 2 * weight; - influences.sort( absNumericalSort ); + } - for ( let i = 0; i < 8; i ++ ) { + } - if ( i < length && influences[ i ][ 1 ] ) { + for ( let i = 0; i < weights.length; i ++ ) { - workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; - workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; + weights[ i ] = weights[ i ] / sum; - } else { + } - workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; - workInfluences[ i ][ 1 ] = 0; + blurUniforms[ 'envMap' ].value = targetIn.texture; + blurUniforms[ 'samples' ].value = samples; + blurUniforms[ 'weights' ].value = weights; + blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - } + if ( poleAxis ) { + + blurUniforms[ 'poleAxis' ].value = poleAxis; } - workInfluences.sort( numericalSort ); + const { _lodMax } = this; + blurUniforms[ 'dTheta' ].value = radiansPerPixel; + blurUniforms[ 'mipInt' ].value = _lodMax - lodIn; - const morphTargets = material.morphTargets && geometry.morphAttributes.position; - const morphNormals = material.morphNormals && geometry.morphAttributes.normal; + const outputSize = this._sizeLods[ lodOut ]; + const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); + const y = 4 * ( this._cubeSize - outputSize ); - let morphInfluencesSum = 0; + _setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); + renderer.setRenderTarget( targetOut ); + renderer.render( blurMesh, _flatCamera ); - for ( let i = 0; i < 8; i ++ ) { + } - const influence = workInfluences[ i ]; - const index = influence[ 0 ]; - const value = influence[ 1 ]; +} - if ( index !== Number.MAX_SAFE_INTEGER && value ) { - if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); +function _createPlanes( lodMax ) { - } + const lodPlanes = []; + const sizeLods = []; + const sigmas = []; - if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { + let lod = lodMax; - geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); + const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - } + for ( let i = 0; i < totalLods; i ++ ) { - morphInfluences[ i ] = value; - morphInfluencesSum += value; + const sizeLod = Math.pow( 2, lod ); + sizeLods.push( sizeLod ); + let sigma = 1.0 / sizeLod; - } else { + if ( i > lodMax - LOD_MIN ) { - if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { + sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; - geometry.deleteAttribute( 'morphTarget' + i ); + } else if ( i === 0 ) { - } + sigma = 0; - if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { + } - geometry.deleteAttribute( 'morphNormal' + i ); + sigmas.push( sigma ); - } + const texelSize = 1.0 / ( sizeLod - 1 ); + const min = - texelSize / 2; + const max = 1 + texelSize / 2; + const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; - morphInfluences[ i ] = 0; + const cubeFaces = 6; + const vertices = 6; + const positionSize = 3; + const uvSize = 2; + const faceIndexSize = 1; - } + const position = new Float32Array( positionSize * vertices * cubeFaces ); + const uv = new Float32Array( uvSize * vertices * cubeFaces ); + const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); + + for ( let face = 0; face < cubeFaces; face ++ ) { + + const x = ( face % 3 ) * 2 / 3 - 1; + const y = face > 2 ? 0 : - 1; + const coordinates = [ + x, y, 0, + x + 2 / 3, y, 0, + x + 2 / 3, y + 1, 0, + x, y, 0, + x + 2 / 3, y + 1, 0, + x, y + 1, 0 + ]; + position.set( coordinates, positionSize * vertices * face ); + uv.set( uv1, uvSize * vertices * face ); + const fill = [ face, face, face, face, face, face ]; + faceIndex.set( fill, faceIndexSize * vertices * face ); } - // GLSL shader uses formula baseinfluence * base + sum(target * influence) - // This allows us to switch between absolute morphs and relative morphs without changing shader code - // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) - const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; + const planes = new BufferGeometry(); + planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); + planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); + planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); + lodPlanes.push( planes ); + + if ( lod > LOD_MIN ) { - program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); + lod --; + + } } - return { + return { lodPlanes, sizeLods, sigmas }; - update: update +} - }; +function _createRenderTarget( width, height, params ) { + + const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); + cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; + cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; + cubeUVRenderTarget.scissorTest = true; + return cubeUVRenderTarget; } -function WebGLObjects( gl, geometries, attributes, info ) { +function _setViewport( target, x, y, width, height ) { - let updateMap = new WeakMap(); + target.viewport.set( x, y, width, height ); + target.scissor.set( x, y, width, height ); - function update( object ) { +} - const frame = info.render.frame; +function _getBlurShader( lodMax, width, height ) { - const geometry = object.geometry; - const buffergeometry = geometries.get( object, geometry ); + const weights = new Float32Array( MAX_SAMPLES ); + const poleAxis = new Vector3( 0, 1, 0 ); + const shaderMaterial = new ShaderMaterial( { - // Update once per frame + name: 'SphericalGaussianBlur', - if ( updateMap.get( buffergeometry ) !== frame ) { + defines: { + 'n': MAX_SAMPLES, + 'CUBEUV_TEXEL_WIDTH': 1.0 / width, + 'CUBEUV_TEXEL_HEIGHT': 1.0 / height, + 'CUBEUV_MAX_MIP': `${lodMax}.0`, + }, - geometries.update( buffergeometry ); + uniforms: { + 'envMap': { value: null }, + 'samples': { value: 1 }, + 'weights': { value: weights }, + 'latitudinal': { value: false }, + 'dTheta': { value: 0 }, + 'mipInt': { value: 0 }, + 'poleAxis': { value: poleAxis } + }, - updateMap.set( buffergeometry, frame ); + vertexShader: _getCommonVertexShader(), - } + fragmentShader: /* glsl */` - if ( object.isInstancedMesh ) { + precision mediump float; + precision mediump int; - if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { + varying vec3 vOutputDirection; - object.addEventListener( 'dispose', onInstancedMeshDispose ); + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; - } + #define ENVMAP_TYPE_CUBE_UV + #include - attributes.update( object.instanceMatrix, 34962 ); + vec3 getSample( float theta, vec3 axis ) { - if ( object.instanceColor !== null ) { + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - attributes.update( object.instanceColor, 34962 ); + return bilinearCubeUV( envMap, sampleDirection, mipInt ); } - } + void main() { - return buffergeometry; + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - } - - function dispose() { - - updateMap = new WeakMap(); - - } + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - function onInstancedMeshDispose( event ) { + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - const instancedMesh = event.target; + } - instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); + axis = normalize( axis ); - attributes.remove( instancedMesh.instanceMatrix ); + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); + for ( int i = 1; i < n; i++ ) { - } + if ( i >= samples ) { - return { + break; - update: update, - dispose: dispose + } - }; + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); -} + } -class DataTexture2DArray extends Texture { + } + `, - constructor( data = null, width = 1, height = 1, depth = 1 ) { + blending: NoBlending, + depthTest: false, + depthWrite: false - super( null ); + } ); - this.image = { data, width, height, depth }; + return shaderMaterial; - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; +} - this.wrapR = ClampToEdgeWrapping; +function _getEquirectMaterial() { - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + return new ShaderMaterial( { - this.needsUpdate = true; + name: 'EquirectangularToCubeUV', - } + uniforms: { + 'envMap': { value: null } + }, -} + vertexShader: _getCommonVertexShader(), -DataTexture2DArray.prototype.isDataTexture2DArray = true; + fragmentShader: /* glsl */` -class DataTexture3D extends Texture { + precision mediump float; + precision mediump int; - constructor( data = null, width = 1, height = 1, depth = 1 ) { + varying vec3 vOutputDirection; - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // const texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 + uniform sampler2D envMap; - super( null ); + #include - this.image = { data, width, height, depth }; + void main() { - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); - this.wrapR = ClampToEdgeWrapping; + gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + } + `, - this.needsUpdate = true; + blending: NoBlending, + depthTest: false, + depthWrite: false - } + } ); } -DataTexture3D.prototype.isDataTexture3D = true; - -/** - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ +function _getCubemapMaterial() { -const emptyTexture = new Texture(); -const emptyTexture2dArray = new DataTexture2DArray(); -const emptyTexture3d = new DataTexture3D(); -const emptyCubeTexture = new CubeTexture(); + return new ShaderMaterial( { -// --- Utilities --- + name: 'CubemapToCubeUV', -// Array Caches (provide typed arrays for temporary by size) + uniforms: { + 'envMap': { value: null }, + 'flipEnvMap': { value: - 1 } + }, -const arrayCacheF32 = []; -const arrayCacheI32 = []; + vertexShader: _getCommonVertexShader(), -// Float32Array caches used for uploading Matrix uniforms + fragmentShader: /* glsl */` -const mat4array = new Float32Array( 16 ); -const mat3array = new Float32Array( 9 ); -const mat2array = new Float32Array( 4 ); + precision mediump float; + precision mediump int; -// Flattening for arrays of vectors and matrices + uniform float flipEnvMap; -function flatten( array, nBlocks, blockSize ) { + varying vec3 vOutputDirection; - const firstElem = array[ 0 ]; + uniform samplerCube envMap; - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 + void main() { - const n = nBlocks * blockSize; - let r = arrayCacheF32[ n ]; + gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); - if ( r === undefined ) { + } + `, - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; + blending: NoBlending, + depthTest: false, + depthWrite: false - } + } ); - if ( nBlocks !== 0 ) { +} - firstElem.toArray( r, 0 ); +function _getCommonVertexShader() { - for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { + return /* glsl */` - offset += blockSize; - array[ i ].toArray( r, offset ); + precision mediump float; + precision mediump int; - } + attribute float faceIndex; - } + varying vec3 vOutputDirection; - return r; + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { -} + uv = 2.0 * uv - 1.0; -function arraysEqual( a, b ) { + vec3 direction = vec3( uv, 1.0 ); - if ( a.length !== b.length ) return false; + if ( face == 0.0 ) { - for ( let i = 0, l = a.length; i < l; i ++ ) { + direction = direction.zyx; // ( 1, v, u ) pos x - if ( a[ i ] !== b[ i ] ) return false; + } else if ( face == 1.0 ) { - } + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y - return true; + } else if ( face == 2.0 ) { -} + direction.x *= -1.0; // ( -u, v, 1 ) pos z -function copyArray( a, b ) { + } else if ( face == 3.0 ) { - for ( let i = 0, l = b.length; i < l; i ++ ) { + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x - a[ i ] = b[ i ]; + } else if ( face == 4.0 ) { - } + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y -} + } else if ( face == 5.0 ) { -// Texture unit allocation + direction.z *= -1.0; // ( u, v, -1 ) neg z -function allocTexUnits( textures, n ) { + } - let r = arrayCacheI32[ n ]; + return direction; - if ( r === undefined ) { + } - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; + void main() { - } + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); - for ( let i = 0; i !== n; ++ i ) { + } + `; - r[ i ] = textures.allocateTextureUnit(); +} - } +function WebGLCubeUVMaps( renderer ) { - return r; + let cubeUVmaps = new WeakMap(); -} + let pmremGenerator = null; -// --- Setters --- + function get( texture ) { -// Note: Defining these methods externally, because they come in a bunch -// and this way their names minify. + if ( texture && texture.isTexture ) { -// Single scalar + const mapping = texture.mapping; -function setValueV1f( gl, v ) { + const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); + const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); - const cache = this.cache; + // equirect/cube map to cubeUV conversion - if ( cache[ 0 ] === v ) return; + if ( isEquirectMap || isCubeMap ) { - gl.uniform1f( this.addr, v ); + if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { - cache[ 0 ] = v; + texture.needsPMREMUpdate = false; -} + let renderTarget = cubeUVmaps.get( texture ); -// Single float vector (from flat array or THREE.VectorN) + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); -function setValueV2f( gl, v ) { + renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); + cubeUVmaps.set( texture, renderTarget ); - const cache = this.cache; + return renderTarget.texture; - if ( v.x !== undefined ) { + } else { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { + if ( cubeUVmaps.has( texture ) ) { - gl.uniform2f( this.addr, v.x, v.y ); + return cubeUVmaps.get( texture ).texture; - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; + } else { - } + const image = texture.image; - } else { + if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { - if ( arraysEqual( cache, v ) ) return; + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - gl.uniform2fv( this.addr, v ); + const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); + cubeUVmaps.set( texture, renderTarget ); - copyArray( cache, v ); + texture.addEventListener( 'dispose', onTextureDispose ); - } + return renderTarget.texture; -} + } else { -function setValueV3f( gl, v ) { + // image not yet ready. try the conversion next frame - const cache = this.cache; + return null; - if ( v.x !== undefined ) { + } - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { + } - gl.uniform3f( this.addr, v.x, v.y, v.z ); + } - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; + } } - } else if ( v.r !== undefined ) { + return texture; - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { + } - gl.uniform3f( this.addr, v.r, v.g, v.b ); + function isCubeTextureComplete( image ) { - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; + let count = 0; + const length = 6; - } + for ( let i = 0; i < length; i ++ ) { - } else { + if ( image[ i ] !== undefined ) count ++; - if ( arraysEqual( cache, v ) ) return; + } - gl.uniform3fv( this.addr, v ); + return count === length; - copyArray( cache, v ); } -} - -function setValueV4f( gl, v ) { + function onTextureDispose( event ) { - const cache = this.cache; + const texture = event.target; - if ( v.x !== undefined ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { + const cubemapUV = cubeUVmaps.get( texture ); - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + if ( cubemapUV !== undefined ) { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; + cubeUVmaps.delete( texture ); + cubemapUV.dispose(); } - } else { + } - if ( arraysEqual( cache, v ) ) return; + function dispose() { - gl.uniform4fv( this.addr, v ); + cubeUVmaps = new WeakMap(); - copyArray( cache, v ); + if ( pmremGenerator !== null ) { + + pmremGenerator.dispose(); + pmremGenerator = null; + + } } + return { + get: get, + dispose: dispose + }; + } -// Single matrix (from flat array or THREE.MatrixN) +function WebGLExtensions( gl ) { -function setValueM2( gl, v ) { + const extensions = {}; - const cache = this.cache; - const elements = v.elements; + function getExtension( name ) { - if ( elements === undefined ) { + if ( extensions[ name ] !== undefined ) { - if ( arraysEqual( cache, v ) ) return; + return extensions[ name ]; - gl.uniformMatrix2fv( this.addr, false, v ); + } - copyArray( cache, v ); + let extension; - } else { + switch ( name ) { - if ( arraysEqual( cache, elements ) ) return; + case 'WEBGL_depth_texture': + extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); + break; - mat2array.set( elements ); + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; - gl.uniformMatrix2fv( this.addr, false, mat2array ); + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; - copyArray( cache, elements ); + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - } + default: + extension = gl.getExtension( name ); -} + } -function setValueM3( gl, v ) { + extensions[ name ] = extension; - const cache = this.cache; - const elements = v.elements; + return extension; - if ( elements === undefined ) { + } - if ( arraysEqual( cache, v ) ) return; + return { - gl.uniformMatrix3fv( this.addr, false, v ); + has: function ( name ) { - copyArray( cache, v ); + return getExtension( name ) !== null; - } else { + }, - if ( arraysEqual( cache, elements ) ) return; + init: function ( capabilities ) { - mat3array.set( elements ); + if ( capabilities.isWebGL2 ) { - gl.uniformMatrix3fv( this.addr, false, mat3array ); + getExtension( 'EXT_color_buffer_float' ); - copyArray( cache, elements ); + } else { - } + getExtension( 'WEBGL_depth_texture' ); + getExtension( 'OES_texture_float' ); + getExtension( 'OES_texture_half_float' ); + getExtension( 'OES_texture_half_float_linear' ); + getExtension( 'OES_standard_derivatives' ); + getExtension( 'OES_element_index_uint' ); + getExtension( 'OES_vertex_array_object' ); + getExtension( 'ANGLE_instanced_arrays' ); -} + } -function setValueM4( gl, v ) { + getExtension( 'OES_texture_float_linear' ); + getExtension( 'EXT_color_buffer_half_float' ); + getExtension( 'WEBGL_multisampled_render_to_texture' ); - const cache = this.cache; - const elements = v.elements; + }, - if ( elements === undefined ) { + get: function ( name ) { - if ( arraysEqual( cache, v ) ) return; + const extension = getExtension( name ); - gl.uniformMatrix4fv( this.addr, false, v ); + if ( extension === null ) { - copyArray( cache, v ); + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - } else { + } - if ( arraysEqual( cache, elements ) ) return; + return extension; - mat4array.set( elements ); + } - gl.uniformMatrix4fv( this.addr, false, mat4array ); + }; - copyArray( cache, elements ); +} - } +function WebGLGeometries( gl, attributes, info, bindingStates ) { -} + const geometries = {}; + const wireframeAttributes = new WeakMap(); -// Single integer / boolean + function onGeometryDispose( event ) { -function setValueV1i( gl, v ) { + const geometry = event.target; - const cache = this.cache; + if ( geometry.index !== null ) { - if ( cache[ 0 ] === v ) return; + attributes.remove( geometry.index ); - gl.uniform1i( this.addr, v ); + } - cache[ 0 ] = v; + for ( const name in geometry.attributes ) { -} + attributes.remove( geometry.attributes[ name ] ); -// Single integer / boolean vector (from flat array) + } -function setValueV2i( gl, v ) { + geometry.removeEventListener( 'dispose', onGeometryDispose ); - const cache = this.cache; + delete geometries[ geometry.id ]; - if ( arraysEqual( cache, v ) ) return; + const attribute = wireframeAttributes.get( geometry ); - gl.uniform2iv( this.addr, v ); + if ( attribute ) { - copyArray( cache, v ); + attributes.remove( attribute ); + wireframeAttributes.delete( geometry ); -} + } -function setValueV3i( gl, v ) { + bindingStates.releaseStatesOfGeometry( geometry ); - const cache = this.cache; + if ( geometry.isInstancedBufferGeometry === true ) { - if ( arraysEqual( cache, v ) ) return; + delete geometry._maxInstanceCount; - gl.uniform3iv( this.addr, v ); + } - copyArray( cache, v ); + // -} + info.memory.geometries --; -function setValueV4i( gl, v ) { + } - const cache = this.cache; + function get( object, geometry ) { - if ( arraysEqual( cache, v ) ) return; + if ( geometries[ geometry.id ] === true ) return geometry; - gl.uniform4iv( this.addr, v ); + geometry.addEventListener( 'dispose', onGeometryDispose ); - copyArray( cache, v ); + geometries[ geometry.id ] = true; -} + info.memory.geometries ++; -// Single unsigned integer + return geometry; -function setValueV1ui( gl, v ) { + } - const cache = this.cache; + function update( geometry ) { - if ( cache[ 0 ] === v ) return; + const geometryAttributes = geometry.attributes; - gl.uniform1ui( this.addr, v ); + // Updating index buffer in VAO now. See WebGLBindingStates. - cache[ 0 ] = v; + for ( const name in geometryAttributes ) { -} + attributes.update( geometryAttributes[ name ], 34962 ); -// Single unsigned integer vector (from flat array) + } -function setValueV2ui( gl, v ) { + // morph targets - const cache = this.cache; + const morphAttributes = geometry.morphAttributes; - if ( arraysEqual( cache, v ) ) return; + for ( const name in morphAttributes ) { - gl.uniform2uiv( this.addr, v ); + const array = morphAttributes[ name ]; - copyArray( cache, v ); + for ( let i = 0, l = array.length; i < l; i ++ ) { -} + attributes.update( array[ i ], 34962 ); -function setValueV3ui( gl, v ) { + } - const cache = this.cache; + } - if ( arraysEqual( cache, v ) ) return; + } - gl.uniform3uiv( this.addr, v ); + function updateWireframeAttribute( geometry ) { - copyArray( cache, v ); + const indices = []; -} + const geometryIndex = geometry.index; + const geometryPosition = geometry.attributes.position; + let version = 0; -function setValueV4ui( gl, v ) { + if ( geometryIndex !== null ) { - const cache = this.cache; + const array = geometryIndex.array; + version = geometryIndex.version; - if ( arraysEqual( cache, v ) ) return; + for ( let i = 0, l = array.length; i < l; i += 3 ) { - gl.uniform4uiv( this.addr, v ); + const a = array[ i + 0 ]; + const b = array[ i + 1 ]; + const c = array[ i + 2 ]; - copyArray( cache, v ); + indices.push( a, b, b, c, c, a ); -} + } + } else { -// Single texture (2D / Cube) + const array = geometryPosition.array; + version = geometryPosition.version; -function setValueT1( gl, v, textures ) { + for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const a = i + 0; + const b = i + 1; + const c = i + 2; - if ( cache[ 0 ] !== unit ) { + indices.push( a, b, b, c, c, a ); - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + } - } + } - textures.safeSetTexture2D( v || emptyTexture, unit ); + const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); + attribute.version = version; -} + // Updating index buffer in VAO now. See WebGLBindingStates -function setValueT3D1( gl, v, textures ) { + // - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const previousAttribute = wireframeAttributes.get( geometry ); - if ( cache[ 0 ] !== unit ) { + if ( previousAttribute ) attributes.remove( previousAttribute ); - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + // + + wireframeAttributes.set( geometry, attribute ); } - textures.setTexture3D( v || emptyTexture3d, unit ); + function getWireframeAttribute( geometry ) { -} + const currentAttribute = wireframeAttributes.get( geometry ); -function setValueT6( gl, v, textures ) { + if ( currentAttribute ) { - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + const geometryIndex = geometry.index; - if ( cache[ 0 ] !== unit ) { + if ( geometryIndex !== null ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + // if the attribute is obsolete, create a new one - } + if ( currentAttribute.version < geometryIndex.version ) { - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); + updateWireframeAttribute( geometry ); -} - -function setValueT2DArray1( gl, v, textures ) { - - const cache = this.cache; - const unit = textures.allocateTextureUnit(); + } - if ( cache[ 0 ] !== unit ) { + } - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + } else { - } + updateWireframeAttribute( geometry ); - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); + } -} + return wireframeAttributes.get( geometry ); -// Helper to pick the right setter for the singular case + } -function getSingularSetter( type ) { + return { - switch ( type ) { + get: get, + update: update, - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 + getWireframeAttribute: getWireframeAttribute - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 + }; - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 +} - case 0x1405: return setValueV1ui; // UINT - case 0x8dc6: return setValueV2ui; // _VEC2 - case 0x8dc7: return setValueV3ui; // _VEC3 - case 0x8dc8: return setValueV4ui; // _VEC4 +function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1; + const isWebGL2 = capabilities.isWebGL2; - case 0x8b5f: // SAMPLER_3D - case 0x8dcb: // INT_SAMPLER_3D - case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D - return setValueT3D1; + let mode; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6; + function setMode( value ) { - case 0x8dc1: // SAMPLER_2D_ARRAY - case 0x8dcf: // INT_SAMPLER_2D_ARRAY - case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY - case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW - return setValueT2DArray1; + mode = value; } -} + let type, bytesPerElement; + function setIndex( value ) { -// Array of scalars + type = value.type; + bytesPerElement = value.bytesPerElement; -function setValueV1fArray( gl, v ) { + } - gl.uniform1fv( this.addr, v ); + function render( start, count ) { -} + gl.drawElements( mode, count, type, start * bytesPerElement ); -// Array of vectors (from flat array or array of THREE.VectorN) + info.update( count, mode, 1 ); -function setValueV2fArray( gl, v ) { + } - const data = flatten( v, this.size, 2 ); + function renderInstances( start, count, primcount ) { - gl.uniform2fv( this.addr, data ); + if ( primcount === 0 ) return; -} + let extension, methodName; -function setValueV3fArray( gl, v ) { + if ( isWebGL2 ) { - const data = flatten( v, this.size, 3 ); + extension = gl; + methodName = 'drawElementsInstanced'; - gl.uniform3fv( this.addr, data ); + } else { -} + extension = extensions.get( 'ANGLE_instanced_arrays' ); + methodName = 'drawElementsInstancedANGLE'; -function setValueV4fArray( gl, v ) { + if ( extension === null ) { - const data = flatten( v, this.size, 4 ); + console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - gl.uniform4fv( this.addr, data ); + } -} + } -// Array of matrices (from flat array or array of THREE.MatrixN) + extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); -function setValueM2Array( gl, v ) { + info.update( count, mode, primcount ); - const data = flatten( v, this.size, 4 ); + } - gl.uniformMatrix2fv( this.addr, false, data ); + // + + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; } -function setValueM3Array( gl, v ) { +function WebGLInfo( gl ) { - const data = flatten( v, this.size, 9 ); + const memory = { + geometries: 0, + textures: 0 + }; - gl.uniformMatrix3fv( this.addr, false, data ); + const render = { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0 + }; -} + function update( count, mode, instanceCount ) { -function setValueM4Array( gl, v ) { + render.calls ++; - const data = flatten( v, this.size, 16 ); + switch ( mode ) { - gl.uniformMatrix4fv( this.addr, false, data ); + case 4: + render.triangles += instanceCount * ( count / 3 ); + break; -} + case 1: + render.lines += instanceCount * ( count / 2 ); + break; -// Array of integer / boolean + case 3: + render.lines += instanceCount * ( count - 1 ); + break; -function setValueV1iArray( gl, v ) { + case 2: + render.lines += instanceCount * count; + break; - gl.uniform1iv( this.addr, v ); + case 0: + render.points += instanceCount * count; + break; -} + default: + console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); + break; -// Array of integer / boolean vectors (from flat array) + } -function setValueV2iArray( gl, v ) { + } - gl.uniform2iv( this.addr, v ); + function reset() { -} + render.frame ++; + render.calls = 0; + render.triangles = 0; + render.points = 0; + render.lines = 0; -function setValueV3iArray( gl, v ) { + } - gl.uniform3iv( this.addr, v ); + return { + memory: memory, + render: render, + programs: null, + autoReset: true, + reset: reset, + update: update + }; } -function setValueV4iArray( gl, v ) { +function numericalSort( a, b ) { - gl.uniform4iv( this.addr, v ); + return a[ 0 ] - b[ 0 ]; } -// Array of unsigned integer - -function setValueV1uiArray( gl, v ) { +function absNumericalSort( a, b ) { - gl.uniform1uiv( this.addr, v ); + return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); } -// Array of unsigned integer vectors (from flat array) - -function setValueV2uiArray( gl, v ) { - - gl.uniform2uiv( this.addr, v ); +function denormalize( morph, attribute ) { -} + let denominator = 1; + const array = attribute.isInterleavedBufferAttribute ? attribute.data.array : attribute.array; -function setValueV3uiArray( gl, v ) { + if ( array instanceof Int8Array ) denominator = 127; + else if ( array instanceof Int16Array ) denominator = 32767; + else if ( array instanceof Int32Array ) denominator = 2147483647; + else console.error( 'THREE.WebGLMorphtargets: Unsupported morph attribute data type: ', array ); - gl.uniform3uiv( this.addr, v ); + morph.divideScalar( denominator ); } -function setValueV4uiArray( gl, v ) { +function WebGLMorphtargets( gl, capabilities, textures ) { - gl.uniform4uiv( this.addr, v ); + const influencesList = {}; + const morphInfluences = new Float32Array( 8 ); + const morphTextures = new WeakMap(); + const morph = new Vector4(); -} + const workInfluences = []; + for ( let i = 0; i < 8; i ++ ) { -// Array of textures (2D / Cube) + workInfluences[ i ] = [ i, 0 ]; -function setValueT1Array( gl, v, textures ) { + } - const n = v.length; + function update( object, geometry, material, program ) { - const units = allocTexUnits( textures, n ); + const objectInfluences = object.morphTargetInfluences; - gl.uniform1iv( this.addr, units ); + if ( capabilities.isWebGL2 === true ) { - for ( let i = 0; i !== n; ++ i ) { + // instead of using attributes, the WebGL 2 code path encodes morph targets + // into an array of data textures. Each layer represents a single morph target. - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - } + let entry = morphTextures.get( geometry ); -} + if ( entry === undefined || entry.count !== morphTargetsCount ) { -function setValueT6Array( gl, v, textures ) { + if ( entry !== undefined ) entry.texture.dispose(); - const n = v.length; + const hasMorphPosition = geometry.morphAttributes.position !== undefined; + const hasMorphNormals = geometry.morphAttributes.normal !== undefined; + const hasMorphColors = geometry.morphAttributes.color !== undefined; - const units = allocTexUnits( textures, n ); + const morphTargets = geometry.morphAttributes.position || []; + const morphNormals = geometry.morphAttributes.normal || []; + const morphColors = geometry.morphAttributes.color || []; - gl.uniform1iv( this.addr, units ); + let vertexDataCount = 0; - for ( let i = 0; i !== n; ++ i ) { + if ( hasMorphPosition === true ) vertexDataCount = 1; + if ( hasMorphNormals === true ) vertexDataCount = 2; + if ( hasMorphColors === true ) vertexDataCount = 3; - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); + let width = geometry.attributes.position.count * vertexDataCount; + let height = 1; - } + if ( width > capabilities.maxTextureSize ) { -} + height = Math.ceil( width / capabilities.maxTextureSize ); + width = capabilities.maxTextureSize; -// Helper to pick the right setter for a pure (bottom-level) array + } -function getPureArraySetter( type ) { + const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); - switch ( type ) { + const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); + texture.type = FloatType; + texture.needsUpdate = true; - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 + // fill buffer - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 + const vertexDataStride = vertexDataCount * 4; - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 + for ( let i = 0; i < morphTargetsCount; i ++ ) { - case 0x1405: return setValueV1uiArray; // UINT - case 0x8dc6: return setValueV2uiArray; // _VEC2 - case 0x8dc7: return setValueV3uiArray; // _VEC3 - case 0x8dc8: return setValueV4uiArray; // _VEC4 + const morphTarget = morphTargets[ i ]; + const morphNormal = morphNormals[ i ]; + const morphColor = morphColors[ i ]; - case 0x8b5e: // SAMPLER_2D - case 0x8d66: // SAMPLER_EXTERNAL_OES - case 0x8dca: // INT_SAMPLER_2D - case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D - case 0x8b62: // SAMPLER_2D_SHADOW - return setValueT1Array; + const offset = width * height * 4 * i; - case 0x8b60: // SAMPLER_CUBE - case 0x8dcc: // INT_SAMPLER_CUBE - case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE - case 0x8dc5: // SAMPLER_CUBE_SHADOW - return setValueT6Array; + for ( let j = 0; j < morphTarget.count; j ++ ) { - } + const stride = j * vertexDataStride; -} + if ( hasMorphPosition === true ) { -// --- Uniform Classes --- + morph.fromBufferAttribute( morphTarget, j ); -function SingleUniform( id, activeInfo, addr ) { + if ( morphTarget.normalized === true ) denormalize( morph, morphTarget ); - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); + buffer[ offset + stride + 0 ] = morph.x; + buffer[ offset + stride + 1 ] = morph.y; + buffer[ offset + stride + 2 ] = morph.z; + buffer[ offset + stride + 3 ] = 0; - // this.path = activeInfo.name; // DEBUG + } -} + if ( hasMorphNormals === true ) { -function PureArrayUniform( id, activeInfo, addr ) { + morph.fromBufferAttribute( morphNormal, j ); - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); + if ( morphNormal.normalized === true ) denormalize( morph, morphNormal ); - // this.path = activeInfo.name; // DEBUG + buffer[ offset + stride + 4 ] = morph.x; + buffer[ offset + stride + 5 ] = morph.y; + buffer[ offset + stride + 6 ] = morph.z; + buffer[ offset + stride + 7 ] = 0; -} + } -PureArrayUniform.prototype.updateCache = function ( data ) { + if ( hasMorphColors === true ) { - const cache = this.cache; + morph.fromBufferAttribute( morphColor, j ); - if ( data instanceof Float32Array && cache.length !== data.length ) { + if ( morphColor.normalized === true ) denormalize( morph, morphColor ); - this.cache = new Float32Array( data.length ); + buffer[ offset + stride + 8 ] = morph.x; + buffer[ offset + stride + 9 ] = morph.y; + buffer[ offset + stride + 10 ] = morph.z; + buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; - } + } - copyArray( cache, data ); + } -}; + } -function StructuredUniform( id ) { + entry = { + count: morphTargetsCount, + texture: texture, + size: new Vector2( width, height ) + }; - this.id = id; + morphTextures.set( geometry, entry ); - this.seq = []; - this.map = {}; + function disposeTexture() { -} + texture.dispose(); -StructuredUniform.prototype.setValue = function ( gl, value, textures ) { + morphTextures.delete( geometry ); - const seq = this.seq; + geometry.removeEventListener( 'dispose', disposeTexture ); - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + } - const u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); + geometry.addEventListener( 'dispose', disposeTexture ); - } + } -}; + // -// --- Top-level --- + let morphInfluencesSum = 0; -// Parser - builds up the property tree from the path strings + for ( let i = 0; i < objectInfluences.length; i ++ ) { -const RePathPart = /(\w+)(\])?(\[|\.)?/g; + morphInfluencesSum += objectInfluences[ i ]; -// extracts -// - the identifier (member name or array index) -// - followed by an optional right bracket (found when array index) -// - followed by an optional left bracket or dot (type of subscript) -// -// Note: These portions can be read in a non-overlapping fashion and -// allow straightforward parsing of the hierarchy that WebGL encodes -// in the uniform names. + } -function addUniform( container, uniformObject ) { + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences ); -} + program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures ); + program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size ); -function parseUniform( activeInfo, addr, container ) { - const path = activeInfo.name, - pathLength = path.length; + } else { - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; + // When object doesn't have morph target influences defined, we treat it as a 0-length array + // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - while ( true ) { + const length = objectInfluences === undefined ? 0 : objectInfluences.length; - const match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex; + let influences = influencesList[ geometry.id ]; - let id = match[ 1 ]; - const idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; + if ( influences === undefined || influences.length !== length ) { - if ( idIsIndex ) id = id | 0; // convert to integer + // initialise list - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { + influences = []; - // bare name or "pure" bottom-level array "[0]" suffix + for ( let i = 0; i < length; i ++ ) { - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); + influences[ i ] = [ i, 0 ]; - break; + } - } else { + influencesList[ geometry.id ] = influences; - // step into inner node / create it in case it doesn't exist + } - const map = container.map; - let next = map[ id ]; + // Collect influences - if ( next === undefined ) { + for ( let i = 0; i < length; i ++ ) { - next = new StructuredUniform( id ); - addUniform( container, next ); + const influence = influences[ i ]; - } + influence[ 0 ] = i; + influence[ 1 ] = objectInfluences[ i ]; - container = next; + } - } + influences.sort( absNumericalSort ); - } + for ( let i = 0; i < 8; i ++ ) { -} + if ( i < length && influences[ i ][ 1 ] ) { -// Root Container + workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; + workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; -function WebGLUniforms( gl, program ) { + } else { - this.seq = []; - this.map = {}; + workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; + workInfluences[ i ][ 1 ] = 0; - const n = gl.getProgramParameter( program, 35718 ); + } - for ( let i = 0; i < n; ++ i ) { + } - const info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); + workInfluences.sort( numericalSort ); - parseUniform( info, addr, this ); + const morphTargets = geometry.morphAttributes.position; + const morphNormals = geometry.morphAttributes.normal; - } + let morphInfluencesSum = 0; -} + for ( let i = 0; i < 8; i ++ ) { -WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { + const influence = workInfluences[ i ]; + const index = influence[ 0 ]; + const value = influence[ 1 ]; - const u = this.map[ name ]; + if ( index !== Number.MAX_SAFE_INTEGER && value ) { - if ( u !== undefined ) u.setValue( gl, value, textures ); + if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { -}; + geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); -WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { + } - const v = object[ name ]; + if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - if ( v !== undefined ) this.setValue( gl, name, v ); + geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); -}; + } + morphInfluences[ i ] = value; + morphInfluencesSum += value; -// Static interface + } else { -WebGLUniforms.upload = function ( gl, seq, values, textures ) { + if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + geometry.deleteAttribute( 'morphTarget' + i ); - const u = seq[ i ], - v = values[ u.id ]; + } - if ( v.needsUpdate !== false ) { + if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); + geometry.deleteAttribute( 'morphNormal' + i ); - } + } - } + morphInfluences[ i ] = 0; -}; + } -WebGLUniforms.seqWithValue = function ( seq, values ) { + } - const r = []; + // GLSL shader uses formula baseinfluence * base + sum(target * influence) + // This allows us to switch between absolute morphs and relative morphs without changing shader code + // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - for ( let i = 0, n = seq.length; i !== n; ++ i ) { + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); - const u = seq[ i ]; - if ( u.id in values ) r.push( u ); + } } - return r; - -}; - -function WebGLShader( gl, type, string ) { - - const shader = gl.createShader( type ); + return { - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + update: update - return shader; + }; } -let programIdCount = 0; - -function addLineNumbers( string ) { - - const lines = string.split( '\n' ); - - for ( let i = 0; i < lines.length; i ++ ) { - - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; +function WebGLObjects( gl, geometries, attributes, info ) { - } + let updateMap = new WeakMap(); - return lines.join( '\n' ); + function update( object ) { -} + const frame = info.render.frame; -function getEncodingComponents( encoding ) { + const geometry = object.geometry; + const buffergeometry = geometries.get( object, geometry ); - switch ( encoding ) { + // Update once per frame - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); - return [ 'Linear', '( value )' ]; + if ( updateMap.get( buffergeometry ) !== frame ) { - } + geometries.update( buffergeometry ); -} + updateMap.set( buffergeometry, frame ); -function getShaderErrors( gl, shader, type ) { + } - const status = gl.getShaderParameter( shader, 35713 ); - const log = gl.getShaderInfoLog( shader ).trim(); + if ( object.isInstancedMesh ) { - if ( status && log === '' ) return ''; + if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + object.addEventListener( 'dispose', onInstancedMeshDispose ); - const source = gl.getShaderSource( shader ); + } - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); + attributes.update( object.instanceMatrix, 34962 ); -} + if ( object.instanceColor !== null ) { -function getTexelDecodingFunction( functionName, encoding ) { + attributes.update( object.instanceColor, 34962 ); - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; + } -} + } -function getTexelEncodingFunction( functionName, encoding ) { + return buffergeometry; - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; + } -} + function dispose() { -function getToneMappingFunction( functionName, toneMapping ) { + updateMap = new WeakMap(); - let toneMappingName; + } - switch ( toneMapping ) { + function onInstancedMeshDispose( event ) { - case LinearToneMapping: - toneMappingName = 'Linear'; - break; + const instancedMesh = event.target; - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; + instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; + attributes.remove( instancedMesh.instanceMatrix ); - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; + if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); - case CustomToneMapping: - toneMappingName = 'Custom'; - break; + } - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; + return { - } + update: update, + dispose: dispose - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + }; } -function generateExtensions( parameters ) { - - const chunks = [ - ( parameters.extensionDerivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', - ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; +/** + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [textures] ) + * + * uploads a uniform value(s) + * the 'textures' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (textures factorizations): + * + * .upload( gl, seq, values, textures ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * + * Methods of the top-level container (textures factorizations): + * + * .setValue( gl, name, value, textures ) + * + * sets uniform with name 'name' to 'value' + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + */ - return chunks.filter( filterEmptyLine ).join( '\n' ); +const emptyTexture = new Texture(); +const emptyArrayTexture = new DataArrayTexture(); +const empty3dTexture = new Data3DTexture(); +const emptyCubeTexture = new CubeTexture(); -} +// --- Utilities --- -function generateDefines( defines ) { +// Array Caches (provide typed arrays for temporary by size) - const chunks = []; +const arrayCacheF32 = []; +const arrayCacheI32 = []; - for ( const name in defines ) { +// Float32Array caches used for uploading Matrix uniforms - const value = defines[ name ]; +const mat4array = new Float32Array( 16 ); +const mat3array = new Float32Array( 9 ); +const mat2array = new Float32Array( 4 ); - if ( value === false ) continue; +// Flattening for arrays of vectors and matrices - chunks.push( '#define ' + name + ' ' + value ); +function flatten( array, nBlocks, blockSize ) { - } + const firstElem = array[ 0 ]; - return chunks.join( '\n' ); + if ( firstElem <= 0 || firstElem > 0 ) return array; + // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 -} + const n = nBlocks * blockSize; + let r = arrayCacheF32[ n ]; -function fetchAttributeLocations( gl, program ) { + if ( r === undefined ) { - const attributes = {}; + r = new Float32Array( n ); + arrayCacheF32[ n ] = r; - const n = gl.getProgramParameter( program, 35721 ); + } - for ( let i = 0; i < n; i ++ ) { + if ( nBlocks !== 0 ) { - const info = gl.getActiveAttrib( program, i ); - const name = info.name; + firstElem.toArray( r, 0 ); - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { + + offset += blockSize; + array[ i ].toArray( r, offset ); - attributes[ name ] = gl.getAttribLocation( program, name ); + } } - return attributes; + return r; } -function filterEmptyLine( string ) { - - return string !== ''; - -} +function arraysEqual( a, b ) { -function replaceLightNums( string, parameters ) { + if ( a.length !== b.length ) return false; - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); + for ( let i = 0, l = a.length; i < l; i ++ ) { -} + if ( a[ i ] !== b[ i ] ) return false; -function replaceClippingPlaneNums( string, parameters ) { + } - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); + return true; } -// Resolve Includes +function copyArray( a, b ) { -const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; + for ( let i = 0, l = b.length; i < l; i ++ ) { -function resolveIncludes( string ) { + a[ i ] = b[ i ]; - return string.replace( includePattern, includeReplacer ); + } } -function includeReplacer( match, include ) { - - const string = ShaderChunk[ include ]; +// Texture unit allocation - if ( string === undefined ) { +function allocTexUnits( textures, n ) { - throw new Error( 'Can not resolve #include <' + include + '>' ); + let r = arrayCacheI32[ n ]; - } + if ( r === undefined ) { - return resolveIncludes( string ); + r = new Int32Array( n ); + arrayCacheI32[ n ] = r; -} + } -// Unroll Loops + for ( let i = 0; i !== n; ++ i ) { -const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; -const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; + r[ i ] = textures.allocateTextureUnit(); -function unrollLoops( string ) { + } - return string - .replace( unrollLoopPattern, loopReplacer ) - .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); + return r; } -function deprecatedLoopReplacer( match, start, end, snippet ) { - - console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); - return loopReplacer( match, start, end, snippet ); +// --- Setters --- -} +// Note: Defining these methods externally, because they come in a bunch +// and this way their names minify. -function loopReplacer( match, start, end, snippet ) { +// Single scalar - let string = ''; +function setValueV1f( gl, v ) { - for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { + const cache = this.cache; - string += snippet - .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); + if ( cache[ 0 ] === v ) return; - } + gl.uniform1f( this.addr, v ); - return string; + cache[ 0 ] = v; } -// +// Single float vector (from flat array or THREE.VectorN) -function generatePrecision( parameters ) { +function setValueV2f( gl, v ) { - let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; + const cache = this.cache; - if ( parameters.precision === 'highp' ) { + if ( v.x !== undefined ) { - precisionstring += '\n#define HIGH_PRECISION'; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - } else if ( parameters.precision === 'mediump' ) { + gl.uniform2f( this.addr, v.x, v.y ); - precisionstring += '\n#define MEDIUM_PRECISION'; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - } else if ( parameters.precision === 'lowp' ) { + } - precisionstring += '\n#define LOW_PRECISION'; + } else { - } + if ( arraysEqual( cache, v ) ) return; - return precisionstring; + gl.uniform2fv( this.addr, v ); -} + copyArray( cache, v ); -function generateShadowMapTypeDefine( parameters ) { + } - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; +} - if ( parameters.shadowMapType === PCFShadowMap ) { +function setValueV3f( gl, v ) { - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + const cache = this.cache; - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { + if ( v.x !== undefined ) { - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - } else if ( parameters.shadowMapType === VSMShadowMap ) { + gl.uniform3f( this.addr, v.x, v.y, v.z ); - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - } + } - return shadowMapTypeDefine; + } else if ( v.r !== undefined ) { -} + if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { -function generateEnvMapTypeDefine( parameters ) { + gl.uniform3f( this.addr, v.r, v.g, v.b ); - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + cache[ 0 ] = v.r; + cache[ 1 ] = v.g; + cache[ 2 ] = v.b; - if ( parameters.envMap ) { + } - switch ( parameters.envMapMode ) { + } else { - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; + if ( arraysEqual( cache, v ) ) return; - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; + gl.uniform3fv( this.addr, v ); - } + copyArray( cache, v ); } - return envMapTypeDefine; - } -function generateEnvMapModeDefine( parameters ) { +function setValueV4f( gl, v ) { - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + const cache = this.cache; - if ( parameters.envMap ) { + if ( v.x !== undefined ) { - switch ( parameters.envMapMode ) { + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - case CubeRefractionMapping: - case CubeUVRefractionMapping: + gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; } - } + } else { - return envMapModeDefine; + if ( arraysEqual( cache, v ) ) return; + + gl.uniform4fv( this.addr, v ); + + copyArray( cache, v ); + + } } -function generateEnvMapBlendingDefine( parameters ) { +// Single matrix (from flat array or THREE.MatrixN) - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; +function setValueM2( gl, v ) { - if ( parameters.envMap ) { + const cache = this.cache; + const elements = v.elements; - switch ( parameters.combine ) { + if ( elements === undefined ) { - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; + if ( arraysEqual( cache, v ) ) return; - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; + gl.uniformMatrix2fv( this.addr, false, v ); - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; + copyArray( cache, v ); - } + } else { - } + if ( arraysEqual( cache, elements ) ) return; - return envMapBlendingDefine; + mat2array.set( elements ); + + gl.uniformMatrix2fv( this.addr, false, mat2array ); + + copyArray( cache, elements ); + + } } -function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { +function setValueM3( gl, v ) { - const gl = renderer.getContext(); + const cache = this.cache; + const elements = v.elements; - const defines = parameters.defines; + if ( elements === undefined ) { - let vertexShader = parameters.vertexShader; - let fragmentShader = parameters.fragmentShader; + if ( arraysEqual( cache, v ) ) return; - const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); - const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); - const envMapModeDefine = generateEnvMapModeDefine( parameters ); - const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + gl.uniformMatrix3fv( this.addr, false, v ); + copyArray( cache, v ); - const gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + } else { - const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); + if ( arraysEqual( cache, elements ) ) return; - const customDefines = generateDefines( defines ); + mat3array.set( elements ); - const program = gl.createProgram(); + gl.uniformMatrix3fv( this.addr, false, mat3array ); - let prefixVertex, prefixFragment; - let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; + copyArray( cache, elements ); - if ( parameters.isRawShaderMaterial ) { + } - prefixVertex = [ +} - customDefines +function setValueM4( gl, v ) { - ].filter( filterEmptyLine ).join( '\n' ); + const cache = this.cache; + const elements = v.elements; - if ( prefixVertex.length > 0 ) { + if ( elements === undefined ) { - prefixVertex += '\n'; + if ( arraysEqual( cache, v ) ) return; - } + gl.uniformMatrix4fv( this.addr, false, v ); - prefixFragment = [ + copyArray( cache, v ); - customExtensions, - customDefines + } else { - ].filter( filterEmptyLine ).join( '\n' ); + if ( arraysEqual( cache, elements ) ) return; - if ( prefixFragment.length > 0 ) { + mat4array.set( elements ); - prefixFragment += '\n'; + gl.uniformMatrix4fv( this.addr, false, mat4array ); - } + copyArray( cache, elements ); - } else { + } - prefixVertex = [ +} - generatePrecision( parameters ), +// Single integer / boolean - '#define SHADER_NAME ' + parameters.shaderName, +function setValueV1i( gl, v ) { - customDefines, + const cache = this.cache; - parameters.instancing ? '#define USE_INSTANCING' : '', - parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', + if ( cache[ 0 ] === v ) return; - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + gl.uniform1i( this.addr, v ); - '#define GAMMA_FACTOR ' + gammaFactorDefine, + cache[ 0 ] = v; - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', +} - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', +// Single integer / boolean vector (from flat array) - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', +function setValueV2i( gl, v ) { - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + const cache = this.cache; - parameters.flatShading ? '#define FLAT_SHADED' : '', + if ( arraysEqual( cache, v ) ) return; - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + gl.uniform2iv( this.addr, v ); - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + copyArray( cache, v ); - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', +} - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', +function setValueV3i( gl, v ) { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + const cache = this.cache; - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + if ( arraysEqual( cache, v ) ) return; - '#ifdef USE_INSTANCING', + gl.uniform3iv( this.addr, v ); - ' attribute mat4 instanceMatrix;', + copyArray( cache, v ); - '#endif', +} - '#ifdef USE_INSTANCING_COLOR', +function setValueV4i( gl, v ) { - ' attribute vec3 instanceColor;', + const cache = this.cache; - '#endif', + if ( arraysEqual( cache, v ) ) return; - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', + gl.uniform4iv( this.addr, v ); - '#ifdef USE_TANGENT', + copyArray( cache, v ); - ' attribute vec4 tangent;', +} - '#endif', +// Single unsigned integer - '#if defined( USE_COLOR_ALPHA )', +function setValueV1ui( gl, v ) { - ' attribute vec4 color;', + const cache = this.cache; - '#elif defined( USE_COLOR )', + if ( cache[ 0 ] === v ) return; - ' attribute vec3 color;', + gl.uniform1ui( this.addr, v ); - '#endif', + cache[ 0 ] = v; - '#ifdef USE_MORPHTARGETS', +} - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', +// Single unsigned integer vector (from flat array) - ' #ifdef USE_MORPHNORMALS', +function setValueV2ui( gl, v ) { - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', + const cache = this.cache; - ' #else', + if ( arraysEqual( cache, v ) ) return; - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', + gl.uniform2uiv( this.addr, v ); - ' #endif', + copyArray( cache, v ); - '#endif', +} - '#ifdef USE_SKINNING', +function setValueV3ui( gl, v ) { - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', + const cache = this.cache; - '#endif', + if ( arraysEqual( cache, v ) ) return; - '\n' + gl.uniform3uiv( this.addr, v ); - ].filter( filterEmptyLine ).join( '\n' ); + copyArray( cache, v ); - prefixFragment = [ +} - customExtensions, +function setValueV4ui( gl, v ) { - generatePrecision( parameters ), + const cache = this.cache; - '#define SHADER_NAME ' + parameters.shaderName, + if ( arraysEqual( cache, v ) ) return; - customDefines, + gl.uniform4uiv( this.addr, v ); - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer + copyArray( cache, v ); - '#define GAMMA_FACTOR ' + gammaFactorDefine, +} - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', - parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', +// Single texture (2D / Cube) - parameters.sheen ? '#define USE_SHEEN' : '', - parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', +function setValueT1( gl, v, textures ) { - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', - parameters.vertexUvs ? '#define USE_UV' : '', - parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', + if ( cache[ 0 ] !== unit ) { - parameters.flatShading ? '#define FLAT_SHADED' : '', + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + textures.setTexture2D( v || emptyTexture, unit ); - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', +} - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', +function setValueT3D1( gl, v, textures ) { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - ( ( parameters.extensionShaderTextureLOD || parameters.envMap ) && parameters.rendererExtensionShaderTextureLod ) ? '#define TEXTURE_LOD_EXT' : '', + if ( cache[ 0 ] !== unit ) { - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', - 'uniform bool isOrthographic;', + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', + } - parameters.dithering ? '#define DITHERING' : '', + textures.setTexture3D( v || empty3dTexture, unit ); - ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', - getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), +} - parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', +function setValueT6( gl, v, textures ) { - '\n' + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - ].filter( filterEmptyLine ).join( '\n' ); + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; } - vertexShader = resolveIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); + textures.setTextureCube( v || emptyCubeTexture, unit ); - fragmentShader = resolveIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); +} - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); +function setValueT2DArray1( gl, v, textures ) { - if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - // GLSL 3.0 conversion for built-in materials and ShaderMaterial + if ( cache[ 0 ] !== unit ) { - versionString = '#version 300 es\n'; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - prefixVertex = [ - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; + } - prefixFragment = [ - '#define varying in', - ( parameters.glslVersion === GLSL3 ) ? '' : 'out highp vec4 pc_fragColor;', - ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; + textures.setTexture2DArray( v || emptyArrayTexture, unit ); - } +} - const vertexGlsl = versionString + prefixVertex + vertexShader; - const fragmentGlsl = versionString + prefixFragment + fragmentShader; +// Helper to pick the right setter for the singular case - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); +function getSingularSetter( type ) { - const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); + switch ( type ) { - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); + case 0x1406: return setValueV1f; // FLOAT + case 0x8b50: return setValueV2f; // _VEC2 + case 0x8b51: return setValueV3f; // _VEC3 + case 0x8b52: return setValueV4f; // _VEC4 - // Force a particular attribute to index 0. + case 0x8b5a: return setValueM2; // _MAT2 + case 0x8b5b: return setValueM3; // _MAT3 + case 0x8b5c: return setValueM4; // _MAT4 - if ( parameters.index0AttributeName !== undefined ) { + case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); + case 0x1405: return setValueV1ui; // UINT + case 0x8dc6: return setValueV2ui; // _VEC2 + case 0x8dc7: return setValueV3ui; // _VEC3 + case 0x8dc8: return setValueV4ui; // _VEC4 - } else if ( parameters.morphTargets === true ) { + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1; - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3D1; + + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6; + + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArray1; } - gl.linkProgram( program ); +} - // check for link errors - if ( renderer.debug.checkShaderErrors ) { - const programLog = gl.getProgramInfoLog( program ).trim(); - const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); +// Array of scalars - let runnable = true; - let haveDiagnostics = true; +function setValueV1fArray( gl, v ) { - if ( gl.getProgramParameter( program, 35714 ) === false ) { + gl.uniform1fv( this.addr, v ); - runnable = false; +} - const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); +// Array of vectors (from flat array or array of THREE.VectorN) - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); +function setValueV2fArray( gl, v ) { - } else if ( programLog !== '' ) { + const data = flatten( v, this.size, 2 ); - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + gl.uniform2fv( this.addr, data ); - } else if ( vertexLog === '' || fragmentLog === '' ) { +} - haveDiagnostics = false; +function setValueV3fArray( gl, v ) { - } + const data = flatten( v, this.size, 3 ); - if ( haveDiagnostics ) { + gl.uniform3fv( this.addr, data ); - this.diagnostics = { +} - runnable: runnable, +function setValueV4fArray( gl, v ) { - programLog: programLog, + const data = flatten( v, this.size, 4 ); - vertexShader: { + gl.uniform4fv( this.addr, data ); - log: vertexLog, - prefix: prefixVertex +} - }, +// Array of matrices (from flat array or array of THREE.MatrixN) - fragmentShader: { +function setValueM2Array( gl, v ) { - log: fragmentLog, - prefix: prefixFragment + const data = flatten( v, this.size, 4 ); - } + gl.uniformMatrix2fv( this.addr, false, data ); - }; +} - } +function setValueM3Array( gl, v ) { - } + const data = flatten( v, this.size, 9 ); - // Clean up + gl.uniformMatrix3fv( this.addr, false, data ); - // Crashes in iOS9 and iOS10. #18402 - // gl.detachShader( program, glVertexShader ); - // gl.detachShader( program, glFragmentShader ); +} - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); +function setValueM4Array( gl, v ) { - // set up caching for uniform locations + const data = flatten( v, this.size, 16 ); - let cachedUniforms; + gl.uniformMatrix4fv( this.addr, false, data ); - this.getUniforms = function () { +} - if ( cachedUniforms === undefined ) { +// Array of integer / boolean - cachedUniforms = new WebGLUniforms( gl, program ); +function setValueV1iArray( gl, v ) { - } + gl.uniform1iv( this.addr, v ); - return cachedUniforms; +} - }; +// Array of integer / boolean vectors (from flat array) - // set up caching for attribute locations +function setValueV2iArray( gl, v ) { - let cachedAttributes; + gl.uniform2iv( this.addr, v ); - this.getAttributes = function () { +} - if ( cachedAttributes === undefined ) { +function setValueV3iArray( gl, v ) { - cachedAttributes = fetchAttributeLocations( gl, program ); + gl.uniform3iv( this.addr, v ); - } +} - return cachedAttributes; +function setValueV4iArray( gl, v ) { - }; + gl.uniform4iv( this.addr, v ); - // free resource +} - this.destroy = function () { +// Array of unsigned integer - bindingStates.releaseStatesOfProgram( this ); +function setValueV1uiArray( gl, v ) { - gl.deleteProgram( program ); - this.program = undefined; + gl.uniform1uiv( this.addr, v ); - }; +} - // +// Array of unsigned integer vectors (from flat array) - this.name = parameters.shaderName; - this.id = programIdCount ++; - this.cacheKey = cacheKey; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; +function setValueV2uiArray( gl, v ) { - return this; + gl.uniform2uiv( this.addr, v ); } -function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) { +function setValueV3uiArray( gl, v ) { - const programs = []; + gl.uniform3uiv( this.addr, v ); - const isWebGL2 = capabilities.isWebGL2; - const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const floatVertexTextures = capabilities.floatVertexTextures; - const maxVertexUniforms = capabilities.maxVertexUniforms; - const vertexTextures = capabilities.vertexTextures; +} - let precision = capabilities.precision; +function setValueV4uiArray( gl, v ) { - const shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'toon', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; + gl.uniform4uiv( this.addr, v ); - const parameterNames = [ - 'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', - 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', - 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap', - 'roughnessMap', 'metalnessMap', 'gradientMap', - 'alphaMap', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', - 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', - 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha', - 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', - 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', - 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', - 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', - 'sheen', 'transmissionMap' - ]; +} - function getMaxBones( object ) { - const skeleton = object.skeleton; - const bones = skeleton.bones; +// Array of textures (2D / 3D / Cube / 2DArray) - if ( floatVertexTextures ) { +function setValueT1Array( gl, v, textures ) { - return 1024; + const n = v.length; - } else { + const units = allocTexUnits( textures, n ); - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) + gl.uniform1iv( this.addr, units ); - const nVertexUniforms = maxVertexUniforms; - const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + for ( let i = 0; i !== n; ++ i ) { - const maxBones = Math.min( nVertexMatrices, bones.length ); + textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); - if ( maxBones < bones.length ) { + } - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; +} - } +function setValueT3DArray( gl, v, textures ) { - return maxBones; + const n = v.length; - } + const units = allocTexUnits( textures, n ); - } + gl.uniform1iv( this.addr, units ); - function getTextureEncodingFromMap( map ) { + for ( let i = 0; i !== n; ++ i ) { - let encoding; + textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); - if ( map && map.isTexture ) { + } - encoding = map.encoding; +} - } else if ( map && map.isWebGLRenderTarget ) { +function setValueT6Array( gl, v, textures ) { - console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); - encoding = map.texture.encoding; + const n = v.length; - } else { + const units = allocTexUnits( textures, n ); - encoding = LinearEncoding; + gl.uniform1iv( this.addr, units ); - } + for ( let i = 0; i !== n; ++ i ) { - return encoding; + textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } - function getParameters( material, lights, shadows, scene, object ) { +} - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; +function setValueT2DArrayArray( gl, v, textures ) { - const envMap = cubemaps.get( material.envMap || environment ); + const n = v.length; - const shaderID = shaderIDs[ material.type ]; + const units = allocTexUnits( textures, n ); - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + gl.uniform1iv( this.addr, units ); - const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; + for ( let i = 0; i !== n; ++ i ) { - if ( material.precision !== null ) { + textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); - precision = capabilities.getMaxPrecision( material.precision ); + } - if ( precision !== material.precision ) { +} - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - } +// Helper to pick the right setter for a pure (bottom-level) array - } +function getPureArraySetter( type ) { - let vertexShader, fragmentShader; + switch ( type ) { - if ( shaderID ) { + case 0x1406: return setValueV1fArray; // FLOAT + case 0x8b50: return setValueV2fArray; // _VEC2 + case 0x8b51: return setValueV3fArray; // _VEC3 + case 0x8b52: return setValueV4fArray; // _VEC4 - const shader = ShaderLib[ shaderID ]; + case 0x8b5a: return setValueM2Array; // _MAT2 + case 0x8b5b: return setValueM3Array; // _MAT3 + case 0x8b5c: return setValueM4Array; // _MAT4 - vertexShader = shader.vertexShader; - fragmentShader = shader.fragmentShader; + case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - } else { + case 0x1405: return setValueV1uiArray; // UINT + case 0x8dc6: return setValueV2uiArray; // _VEC2 + case 0x8dc7: return setValueV3uiArray; // _VEC3 + case 0x8dc8: return setValueV4uiArray; // _VEC4 - vertexShader = material.vertexShader; - fragmentShader = material.fragmentShader; + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1Array; - } + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3DArray; - const currentRenderTarget = renderer.getRenderTarget(); + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6Array; - const parameters = { + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArrayArray; - isWebGL2: isWebGL2, + } - shaderID: shaderID, - shaderName: material.type, +} - vertexShader: vertexShader, - fragmentShader: fragmentShader, - defines: material.defines, +// --- Uniform Classes --- - isRawShaderMaterial: material.isRawShaderMaterial === true, - glslVersion: material.glslVersion, +function SingleUniform( id, activeInfo, addr ) { - precision: precision, + this.id = id; + this.addr = addr; + this.cache = []; + this.setValue = getSingularSetter( activeInfo.type ); - instancing: object.isInstancedMesh === true, - instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, + // this.path = activeInfo.name; // DEBUG - supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), - envMap: !! envMap, - envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), - envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatMap: !! material.clearcoatMap, - clearcoatRoughnessMap: !! material.clearcoatRoughnessMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, +} - gradientMap: !! material.gradientMap, +function PureArrayUniform( id, activeInfo, addr ) { - sheen: !! material.sheen, + this.id = id; + this.addr = addr; + this.cache = []; + this.size = activeInfo.size; + this.setValue = getPureArraySetter( activeInfo.type ); - transmissionMap: !! material.transmissionMap, + // this.path = activeInfo.name; // DEBUG - combine: material.combine, +} - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexAlphas: material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap, - uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmissionMap ) && !! material.displacementMap, +PureArrayUniform.prototype.updateCache = function ( data ) { - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), + const cache = this.cache; - flatShading: !! material.flatShading, + if ( data instanceof Float32Array && cache.length !== data.length ) { - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: logarithmicDepthBuffer, + this.cache = new Float32Array( data.length ); - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: floatVertexTextures, + } - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, + copyArray( cache, data ); - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, +}; - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, +function StructuredUniform( id ) { - numClippingPlanes: clipping.numPlanes, - numClipIntersection: clipping.numIntersection, + this.id = id; - dithering: material.dithering, + this.seq = []; + this.map = {}; - shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, +} - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, +StructuredUniform.prototype.setValue = function ( gl, value, textures ) { - premultipliedAlpha: material.premultipliedAlpha, + const seq = this.seq; - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, + const u = seq[ i ]; + u.setValue( gl, value[ u.id ], textures ); - index0AttributeName: material.index0AttributeName, + } - extensionDerivatives: material.extensions && material.extensions.derivatives, - extensionFragDepth: material.extensions && material.extensions.fragDepth, - extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, - extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, +}; - rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), - rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), - rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), +// --- Top-level --- - customProgramCacheKey: material.customProgramCacheKey() +// Parser - builds up the property tree from the path strings - }; +const RePathPart = /(\w+)(\])?(\[|\.)?/g; - return parameters; +// extracts +// - the identifier (member name or array index) +// - followed by an optional right bracket (found when array index) +// - followed by an optional left bracket or dot (type of subscript) +// +// Note: These portions can be read in a non-overlapping fashion and +// allow straightforward parsing of the hierarchy that WebGL encodes +// in the uniform names. - } +function addUniform( container, uniformObject ) { - function getProgramCacheKey( parameters ) { + container.seq.push( uniformObject ); + container.map[ uniformObject.id ] = uniformObject; - const array = []; +} - if ( parameters.shaderID ) { +function parseUniform( activeInfo, addr, container ) { - array.push( parameters.shaderID ); + const path = activeInfo.name, + pathLength = path.length; - } else { + // reset RegExp object, because of the early exit of a previous run + RePathPart.lastIndex = 0; - array.push( parameters.fragmentShader ); - array.push( parameters.vertexShader ); + while ( true ) { - } + const match = RePathPart.exec( path ), + matchEnd = RePathPart.lastIndex; - if ( parameters.defines !== undefined ) { + let id = match[ 1 ]; + const idIsIndex = match[ 2 ] === ']', + subscript = match[ 3 ]; - for ( const name in parameters.defines ) { + if ( idIsIndex ) id = id | 0; // convert to integer - array.push( name ); - array.push( parameters.defines[ name ] ); + if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - } + // bare name or "pure" bottom-level array "[0]" suffix - } + addUniform( container, subscript === undefined ? + new SingleUniform( id, activeInfo, addr ) : + new PureArrayUniform( id, activeInfo, addr ) ); - if ( parameters.isRawShaderMaterial === false ) { + break; - for ( let i = 0; i < parameterNames.length; i ++ ) { + } else { - array.push( parameters[ parameterNames[ i ] ] ); + // step into inner node / create it in case it doesn't exist - } + const map = container.map; + let next = map[ id ]; - array.push( renderer.outputEncoding ); - array.push( renderer.gammaFactor ); + if ( next === undefined ) { - } + next = new StructuredUniform( id ); + addUniform( container, next ); - array.push( parameters.customProgramCacheKey ); + } - return array.join(); + container = next; + + } } - function getUniforms( material ) { +} - const shaderID = shaderIDs[ material.type ]; - let uniforms; +// Root Container - if ( shaderID ) { +function WebGLUniforms( gl, program ) { - const shader = ShaderLib[ shaderID ]; - uniforms = UniformsUtils.clone( shader.uniforms ); + this.seq = []; + this.map = {}; - } else { + const n = gl.getProgramParameter( program, 35718 ); - uniforms = material.uniforms; + for ( let i = 0; i < n; ++ i ) { - } + const info = gl.getActiveUniform( program, i ), + addr = gl.getUniformLocation( program, info.name ); - return uniforms; + parseUniform( info, addr, this ); } - function acquireProgram( parameters, cacheKey ) { - - let program; - - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { +} - const preexistingProgram = programs[ p ]; +WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { - if ( preexistingProgram.cacheKey === cacheKey ) { + const u = this.map[ name ]; - program = preexistingProgram; - ++ program.usedTimes; + if ( u !== undefined ) u.setValue( gl, value, textures ); - break; +}; - } +WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { - } + const v = object[ name ]; - if ( program === undefined ) { + if ( v !== undefined ) this.setValue( gl, name, v ); - program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); - programs.push( program ); +}; - } - return program; +// Static interface - } +WebGLUniforms.upload = function ( gl, seq, values, textures ) { - function releaseProgram( program ) { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - if ( -- program.usedTimes === 0 ) { + const u = seq[ i ], + v = values[ u.id ]; - // Remove from unordered set - const i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); + if ( v.needsUpdate !== false ) { - // Free WebGL resources - program.destroy(); + // note: always updating when .needsUpdate is undefined + u.setValue( gl, v.value, textures ); } } - return { - getParameters: getParameters, - getProgramCacheKey: getProgramCacheKey, - getUniforms: getUniforms, - acquireProgram: acquireProgram, - releaseProgram: releaseProgram, - // Exposed for resource monitoring & error feedback via renderer.info: - programs: programs - }; +}; -} +WebGLUniforms.seqWithValue = function ( seq, values ) { -function WebGLProperties() { + const r = []; - let properties = new WeakMap(); + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - function get( object ) { + const u = seq[ i ]; + if ( u.id in values ) r.push( u ); - let map = properties.get( object ); + } - if ( map === undefined ) { + return r; - map = {}; - properties.set( object, map ); +}; - } +function WebGLShader( gl, type, string ) { - return map; + const shader = gl.createShader( type ); - } + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - function remove( object ) { + return shader; - properties.delete( object ); +} - } +let programIdCount = 0; - function update( object, key, value ) { +function handleSource( string, errorLine ) { - properties.get( object )[ key ] = value; + const lines = string.split( '\n' ); + const lines2 = []; - } + const from = Math.max( errorLine - 6, 0 ); + const to = Math.min( errorLine + 6, lines.length ); - function dispose() { + for ( let i = from; i < to; i ++ ) { - properties = new WeakMap(); + lines2.push( ( i + 1 ) + ': ' + lines[ i ] ); } - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; + return lines2.join( '\n' ); } -function painterSortStable( a, b ) { +function getEncodingComponents( encoding ) { - if ( a.groupOrder !== b.groupOrder ) { + switch ( encoding ) { - return a.groupOrder - b.groupOrder; + case LinearEncoding: + return [ 'Linear', '( value )' ]; + case sRGBEncoding: + return [ 'sRGB', '( value )' ]; + default: + console.warn( 'THREE.WebGLProgram: Unsupported encoding:', encoding ); + return [ 'Linear', '( value )' ]; - } else if ( a.renderOrder !== b.renderOrder ) { + } - return a.renderOrder - b.renderOrder; +} - } else if ( a.program !== b.program ) { +function getShaderErrors( gl, shader, type ) { - return a.program.id - b.program.id; + const status = gl.getShaderParameter( shader, 35713 ); + const errors = gl.getShaderInfoLog( shader ).trim(); - } else if ( a.material.id !== b.material.id ) { + if ( status && errors === '' ) return ''; - return a.material.id - b.material.id; + const errorLine = parseInt( /ERROR: 0:(\d+)/.exec( errors )[ 1 ] ); - } else if ( a.z !== b.z ) { + // --enable-privileged-webgl-extension + // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - return a.z - b.z; + return type.toUpperCase() + '\n\n' + errors + '\n\n' + handleSource( gl.getShaderSource( shader ), errorLine ); - } else { +} - return a.id - b.id; +function getTexelEncodingFunction( functionName, encoding ) { - } + const components = getEncodingComponents( encoding ); + return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; } -function reversePainterSortStable( a, b ) { +function getToneMappingFunction( functionName, toneMapping ) { - if ( a.groupOrder !== b.groupOrder ) { + let toneMappingName; - return a.groupOrder - b.groupOrder; + switch ( toneMapping ) { - } else if ( a.renderOrder !== b.renderOrder ) { + case LinearToneMapping: + toneMappingName = 'Linear'; + break; - return a.renderOrder - b.renderOrder; + case ReinhardToneMapping: + toneMappingName = 'Reinhard'; + break; - } else if ( a.z !== b.z ) { + case CineonToneMapping: + toneMappingName = 'OptimizedCineon'; + break; - return b.z - a.z; + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; - } else { + case CustomToneMapping: + toneMappingName = 'Custom'; + break; - return a.id - b.id; + default: + console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); + toneMappingName = 'Linear'; } + return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + } +function generateExtensions( parameters ) { -function WebGLRenderList( properties ) { + const chunks = [ + ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', + ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' + ]; - const renderItems = []; - let renderItemsIndex = 0; + return chunks.filter( filterEmptyLine ).join( '\n' ); - const opaque = []; - const transparent = []; +} - const defaultProgram = { id: - 1 }; +function generateDefines( defines ) { - function init() { + const chunks = []; - renderItemsIndex = 0; + for ( const name in defines ) { - opaque.length = 0; - transparent.length = 0; + const value = defines[ name ]; + + if ( value === false ) continue; + + chunks.push( '#define ' + name + ' ' + value ); } - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { + return chunks.join( '\n' ); - let renderItem = renderItems[ renderItemsIndex ]; - const materialProperties = properties.get( material ); +} - if ( renderItem === undefined ) { +function fetchAttributeLocations( gl, program ) { - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: materialProperties.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; + const attributes = {}; - renderItems[ renderItemsIndex ] = renderItem; + const n = gl.getProgramParameter( program, 35721 ); - } else { + for ( let i = 0; i < n; i ++ ) { - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = materialProperties.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; + const info = gl.getActiveAttrib( program, i ); + const name = info.name; - } + let locationSize = 1; + if ( info.type === 35674 ) locationSize = 2; + if ( info.type === 35675 ) locationSize = 3; + if ( info.type === 35676 ) locationSize = 4; - renderItemsIndex ++; + // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - return renderItem; + attributes[ name ] = { + type: info.type, + location: gl.getAttribLocation( program, name ), + locationSize: locationSize + }; } - function push( object, geometry, material, groupOrder, z, group ) { - - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + return attributes; - ( material.transparent === true ? transparent : opaque ).push( renderItem ); +} - } +function filterEmptyLine( string ) { - function unshift( object, geometry, material, groupOrder, z, group ) { + return string !== ''; - const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); +} - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); +function replaceLightNums( string, parameters ) { - } + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) + .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) + .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) + .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - function sort( customOpaqueSort, customTransparentSort ) { +} - if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); +function replaceClippingPlaneNums( string, parameters ) { - } + return string + .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) + .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - function finish() { +} - // Clear references from inactive renderItems in the list +// Resolve Includes - for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { +const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - const renderItem = renderItems[ i ]; +function resolveIncludes( string ) { - if ( renderItem.id === null ) break; + return string.replace( includePattern, includeReplacer ); - renderItem.id = null; - renderItem.object = null; - renderItem.geometry = null; - renderItem.material = null; - renderItem.program = null; - renderItem.group = null; +} - } +function includeReplacer( match, include ) { - } + const string = ShaderChunk[ include ]; - return { + if ( string === undefined ) { - opaque: opaque, - transparent: transparent, + throw new Error( 'Can not resolve #include <' + include + '>' ); - init: init, - push: push, - unshift: unshift, - finish: finish, + } - sort: sort - }; + return resolveIncludes( string ); } -function WebGLRenderLists( properties ) { - - let lists = new WeakMap(); - - function get( scene, renderCallDepth ) { +// Unroll Loops - let list; +const deprecatedUnrollLoopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; +const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - if ( lists.has( scene ) === false ) { +function unrollLoops( string ) { - list = new WebGLRenderList( properties ); - lists.set( scene, [ list ] ); + return string + .replace( unrollLoopPattern, loopReplacer ) + .replace( deprecatedUnrollLoopPattern, deprecatedLoopReplacer ); - } else { +} - if ( renderCallDepth >= lists.get( scene ).length ) { +function deprecatedLoopReplacer( match, start, end, snippet ) { - list = new WebGLRenderList( properties ); - lists.get( scene ).push( list ); + console.warn( 'WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.' ); + return loopReplacer( match, start, end, snippet ); - } else { +} - list = lists.get( scene )[ renderCallDepth ]; +function loopReplacer( match, start, end, snippet ) { - } + let string = ''; - } + for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - return list; + string += snippet + .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) + .replace( /UNROLLED_LOOP_INDEX/g, i ); } - function dispose() { + return string; - lists = new WeakMap(); +} - } +// - return { - get: get, - dispose: dispose - }; +function generatePrecision( parameters ) { -} + let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; -function UniformsCache() { + if ( parameters.precision === 'highp' ) { - const lights = {}; + precisionstring += '\n#define HIGH_PRECISION'; - return { + } else if ( parameters.precision === 'mediump' ) { - get: function ( light ) { + precisionstring += '\n#define MEDIUM_PRECISION'; - if ( lights[ light.id ] !== undefined ) { + } else if ( parameters.precision === 'lowp' ) { - return lights[ light.id ]; + precisionstring += '\n#define LOW_PRECISION'; - } + } - let uniforms; + return precisionstring; - switch ( light.type ) { +} - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color() - }; - break; +function generateShadowMapTypeDefine( parameters ) { - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0 - }; - break; + let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0 - }; - break; + if ( parameters.shadowMapType === PCFShadowMap ) { - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - }; - break; + } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - } + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - lights[ light.id ] = uniforms; + } else if ( parameters.shadowMapType === VSMShadowMap ) { - return uniforms; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - } + } - }; + return shadowMapTypeDefine; } -function ShadowUniformsCache() { +function generateEnvMapTypeDefine( parameters ) { - const lights = {}; + let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - return { + if ( parameters.envMap ) { - get: function ( light ) { + switch ( parameters.envMapMode ) { - if ( lights[ light.id ] !== undefined ) { + case CubeReflectionMapping: + case CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; - return lights[ light.id ]; + case CubeUVReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; - } + } - let uniforms; + } - switch ( light.type ) { + return envMapTypeDefine; - case 'DirectionalLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; +} - case 'SpotLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; +function generateEnvMapModeDefine( parameters ) { - case 'PointLight': - uniforms = { - shadowBias: 0, - shadowNormalBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; + let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - // TODO (abelnation): set RectAreaLight shadow uniforms + if ( parameters.envMap ) { - } + switch ( parameters.envMapMode ) { - lights[ light.id ] = uniforms; + case CubeRefractionMapping: - return uniforms; + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; } - }; - -} - - - -let nextVersion = 0; - -function shadowCastingLightsFirst( lightA, lightB ) { + } - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); + return envMapModeDefine; } -function WebGLLights( extensions, capabilities ) { +function generateEnvMapBlendingDefine( parameters ) { - const cache = new UniformsCache(); + let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - const shadowCache = ShadowUniformsCache(); + if ( parameters.envMap ) { - const state = { + switch ( parameters.combine ) { - version: 0, + case MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, + case MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 - }, + case AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadow: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadow: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - rectAreaLTC1: null, - rectAreaLTC2: null, - point: [], - pointShadow: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [] + } - }; + } - for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); + return envMapBlendingDefine; - const vector3 = new Vector3(); - const matrix4 = new Matrix4(); - const matrix42 = new Matrix4(); +} - function setup( lights ) { +function generateCubeUVSize( parameters ) { - let r = 0, g = 0, b = 0; + const imageHeight = parameters.envMapCubeUVHeight; - for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); + if ( imageHeight === null ) return null; - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + const maxMip = Math.log2( imageHeight / 32 + 1 ) + 3; - let numDirectionalShadows = 0; - let numPointShadows = 0; - let numSpotShadows = 0; + const texelHeight = 1.0 / imageHeight; - lights.sort( shadowCastingLightsFirst ); + const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) ); - for ( let i = 0, l = lights.length; i < l; i ++ ) { + return { texelWidth, texelHeight, maxMip }; - const light = lights[ i ]; +} - const color = light.color; - const intensity = light.intensity; - const distance = light.distance; +function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + // TODO Send this event to Three.js DevTools + // console.log( 'WebGLProgram', cacheKey ); - if ( light.isAmbientLight ) { + const gl = renderer.getContext(); - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; + const defines = parameters.defines; - } else if ( light.isLightProbe ) { + let vertexShader = parameters.vertexShader; + let fragmentShader = parameters.fragmentShader; - for ( let j = 0; j < 9; j ++ ) { + const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); + const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); + const envMapModeDefine = generateEnvMapModeDefine( parameters ); + const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + const envMapCubeUVSize = generateCubeUVSize( parameters ); - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); + const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - } + const customDefines = generateDefines( defines ); - } else if ( light.isDirectionalLight ) { + const program = gl.createProgram(); - const uniforms = cache.get( light ); + let prefixVertex, prefixFragment; + let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + if ( parameters.isRawShaderMaterial ) { - if ( light.castShadow ) { + prefixVertex = [ - const shadow = light.shadow; + customDefines - const shadowUniforms = shadowCache.get( light ); + ].filter( filterEmptyLine ).join( '\n' ); - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + if ( prefixVertex.length > 0 ) { - state.directionalShadow[ directionalLength ] = shadowUniforms; - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + prefixVertex += '\n'; - numDirectionalShadows ++; + } - } + prefixFragment = [ - state.directional[ directionalLength ] = uniforms; + customExtensions, + customDefines - directionalLength ++; + ].filter( filterEmptyLine ).join( '\n' ); - } else if ( light.isSpotLight ) { + if ( prefixFragment.length > 0 ) { - const uniforms = cache.get( light ); + prefixFragment += '\n'; - uniforms.position.setFromMatrixPosition( light.matrixWorld ); + } - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; + } else { - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; + prefixVertex = [ - if ( light.castShadow ) { + generatePrecision( parameters ), - const shadow = light.shadow; + '#define SHADER_NAME ' + parameters.shaderName, - const shadowUniforms = shadowCache.get( light ); + customDefines, - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; + parameters.instancing ? '#define USE_INSTANCING' : '', + parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - state.spotShadow[ spotLength ] = shadowUniforms; - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - numSpotShadows ++; + '#define MAX_BONES ' + parameters.maxBones, + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - } + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - state.spot[ spotLength ] = uniforms; + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - spotLength ++; + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - } else if ( light.isRectAreaLight ) { + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', - const uniforms = cache.get( light ); + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - state.rectArea[ rectAreaLength ] = uniforms; + parameters.flatShading ? '#define FLAT_SHADED' : '', - rectAreaLength ++; + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - } else if ( light.isPointLight ) { + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + ( parameters.morphColors && parameters.isWebGL2 ) ? '#define USE_MORPHCOLORS' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - const uniforms = cache.get( light ); + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - if ( light.castShadow ) { + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - const shadow = light.shadow; + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - const shadowUniforms = shadowCache.get( light ); + '#ifdef USE_INSTANCING', - shadowUniforms.shadowBias = shadow.bias; - shadowUniforms.shadowNormalBias = shadow.normalBias; - shadowUniforms.shadowRadius = shadow.radius; - shadowUniforms.shadowMapSize = shadow.mapSize; - shadowUniforms.shadowCameraNear = shadow.camera.near; - shadowUniforms.shadowCameraFar = shadow.camera.far; + ' attribute mat4 instanceMatrix;', - state.pointShadow[ pointLength ] = shadowUniforms; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; + '#endif', - numPointShadows ++; + '#ifdef USE_INSTANCING_COLOR', - } + ' attribute vec3 instanceColor;', - state.point[ pointLength ] = uniforms; + '#endif', - pointLength ++; + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - } else if ( light.isHemisphereLight ) { + '#ifdef USE_TANGENT', - const uniforms = cache.get( light ); + ' attribute vec4 tangent;', - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); + '#endif', - state.hemi[ hemiLength ] = uniforms; + '#if defined( USE_COLOR_ALPHA )', - hemiLength ++; + ' attribute vec4 color;', - } + '#elif defined( USE_COLOR )', - } + ' attribute vec3 color;', - if ( rectAreaLength > 0 ) { + '#endif', - if ( capabilities.isWebGL2 ) { + '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )', - // WebGL 2 + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + ' #ifdef USE_MORPHNORMALS', - } else { + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - // WebGL 1 + ' #else', - if ( extensions.has( 'OES_texture_float_linear' ) === true ) { + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; - state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; + ' #endif', - } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { + '#endif', - state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; - state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; + '#ifdef USE_SKINNING', - } else { + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); + '#endif', - } + '\n' - } + ].filter( filterEmptyLine ).join( '\n' ); - } + prefixFragment = [ - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; + customExtensions, - const hash = state.hash; + generatePrecision( parameters ), - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { + '#define SHADER_NAME ' + parameters.shaderName, - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; + customDefines, - state.directionalShadow.length = numDirectionalShadows; - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadow.length = numPointShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadow.length = numSpotShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; + parameters.map ? '#define USE_MAP' : '', + parameters.matcap ? '#define USE_MATCAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '', + envMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', + ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; + parameters.clearcoat ? '#define USE_CLEARCOAT' : '', + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - state.version = nextVersion ++; + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULARINTENSITYMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULARCOLORMAP' : '', + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - } + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.alphaTest ? '#define USE_ALPHATEST' : '', - } + parameters.sheen ? '#define USE_SHEEN' : '', + parameters.sheenColorMap ? '#define USE_SHEENCOLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEENROUGHNESSMAP' : '', - function setupView( lights, camera ) { + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - let directionalLength = 0; - let pointLength = 0; - let spotLength = 0; - let rectAreaLength = 0; - let hemiLength = 0; + parameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '', - const viewMatrix = camera.matrixWorldInverse; + parameters.vertexTangents ? '#define USE_TANGENT' : '', + parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUvs ? '#define USE_UV' : '', + parameters.uvsVertexOnly ? '#define UVS_VERTEX_ONLY' : '', - for ( let i = 0, l = lights.length; i < l; i ++ ) { + parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - const light = lights[ i ]; + parameters.flatShading ? '#define FLAT_SHADED' : '', - if ( light.isDirectionalLight ) { + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - const uniforms = state.directional[ directionalLength ]; + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - directionalLength ++; + parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', - } else if ( light.isSpotLight ) { + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - const uniforms = state.spot[ spotLength ]; + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', + ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below + ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + parameters.dithering ? '#define DITHERING' : '', + parameters.opaque ? '#define OPAQUE' : '', - spotLength ++; + ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below + getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), - } else if ( light.isRectAreaLight ) { + parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - const uniforms = state.rectArea[ rectAreaLength ]; + '\n' - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + ].filter( filterEmptyLine ).join( '\n' ); - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); + } - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + vertexShader = resolveIncludes( vertexShader ); + vertexShader = replaceLightNums( vertexShader, parameters ); + vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); + fragmentShader = resolveIncludes( fragmentShader ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); + fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - rectAreaLength ++; + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); - } else if ( light.isPointLight ) { + if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - const uniforms = state.point[ pointLength ]; + // GLSL 3.0 conversion for built-in materials and ShaderMaterial - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + versionString = '#version 300 es\n'; - pointLength ++; + prefixVertex = [ + 'precision mediump sampler2DArray;', + '#define attribute in', + '#define varying out', + '#define texture2D texture' + ].join( '\n' ) + '\n' + prefixVertex; - } else if ( light.isHemisphereLight ) { - - const uniforms = state.hemi[ hemiLength ]; - - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); - - hemiLength ++; - - } - - } + prefixFragment = [ + '#define varying in', + ( parameters.glslVersion === GLSL3 ) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;', + ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', + '#define gl_FragDepthEXT gl_FragDepth', + '#define texture2D texture', + '#define textureCube texture', + '#define texture2DProj textureProj', + '#define texture2DLodEXT textureLod', + '#define texture2DProjLodEXT textureProjLod', + '#define textureCubeLodEXT textureLod', + '#define texture2DGradEXT textureGrad', + '#define texture2DProjGradEXT textureProjGrad', + '#define textureCubeGradEXT textureGrad' + ].join( '\n' ) + '\n' + prefixFragment; } - return { - setup: setup, - setupView: setupView, - state: state - }; - -} + const vertexGlsl = versionString + prefixVertex + vertexShader; + const fragmentGlsl = versionString + prefixFragment + fragmentShader; -function WebGLRenderState( extensions, capabilities ) { + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); - const lights = new WebGLLights( extensions, capabilities ); + const glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); + const glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); - const lightsArray = []; - const shadowsArray = []; + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); - function init() { + // Force a particular attribute to index 0. - lightsArray.length = 0; - shadowsArray.length = 0; + if ( parameters.index0AttributeName !== undefined ) { - } + gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - function pushLight( light ) { + } else if ( parameters.morphTargets === true ) { - lightsArray.push( light ); + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); } - function pushShadow( shadowLight ) { - - shadowsArray.push( shadowLight ); - - } + gl.linkProgram( program ); - function setupLights() { + // check for link errors + if ( renderer.debug.checkShaderErrors ) { - lights.setup( lightsArray ); + const programLog = gl.getProgramInfoLog( program ).trim(); + const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); + const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - } + let runnable = true; + let haveDiagnostics = true; - function setupLightsView( camera ) { + if ( gl.getProgramParameter( program, 35714 ) === false ) { - lights.setupView( lightsArray, camera ); + runnable = false; - } + const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); + const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - const state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, + console.error( + 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + + 'VALIDATE_STATUS ' + gl.getProgramParameter( program, 35715 ) + '\n\n' + + 'Program Info Log: ' + programLog + '\n' + + vertexErrors + '\n' + + fragmentErrors + ); - lights: lights - }; + } else if ( programLog !== '' ) { - return { - init: init, - state: state, - setupLights: setupLights, - setupLightsView: setupLightsView, + console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); - pushLight: pushLight, - pushShadow: pushShadow - }; + } else if ( vertexLog === '' || fragmentLog === '' ) { -} + haveDiagnostics = false; -function WebGLRenderStates( extensions, capabilities ) { + } - let renderStates = new WeakMap(); + if ( haveDiagnostics ) { - function get( scene, renderCallDepth = 0 ) { + this.diagnostics = { - let renderState; + runnable: runnable, - if ( renderStates.has( scene ) === false ) { + programLog: programLog, - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.set( scene, [ renderState ] ); + vertexShader: { - } else { + log: vertexLog, + prefix: prefixVertex - if ( renderCallDepth >= renderStates.get( scene ).length ) { + }, - renderState = new WebGLRenderState( extensions, capabilities ); - renderStates.get( scene ).push( renderState ); + fragmentShader: { - } else { + log: fragmentLog, + prefix: prefixFragment - renderState = renderStates.get( scene )[ renderCallDepth ]; + } - } + }; } - return renderState; - } - function dispose() { + // Clean up - renderStates = new WeakMap(); + // Crashes in iOS9 and iOS10. #18402 + // gl.detachShader( program, glVertexShader ); + // gl.detachShader( program, glFragmentShader ); - } + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - return { - get: get, - dispose: dispose - }; + // set up caching for uniform locations -} + let cachedUniforms; -/** - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + this.getUniforms = function () { -class MeshDepthMaterial extends Material { + if ( cachedUniforms === undefined ) { - constructor( parameters ) { + cachedUniforms = new WebGLUniforms( gl, program ); - super(); + } - this.type = 'MeshDepthMaterial'; + return cachedUniforms; - this.depthPacking = BasicDepthPacking; + }; - this.skinning = false; - this.morphTargets = false; + // set up caching for attribute locations - this.map = null; + let cachedAttributes; - this.alphaMap = null; + this.getAttributes = function () { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( cachedAttributes === undefined ) { - this.wireframe = false; - this.wireframeLinewidth = 1; + cachedAttributes = fetchAttributeLocations( gl, program ); - this.fog = false; + } - this.setValues( parameters ); + return cachedAttributes; - } + }; - copy( source ) { + // free resource - super.copy( source ); + this.destroy = function () { - this.depthPacking = source.depthPacking; + bindingStates.releaseStatesOfProgram( this ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + gl.deleteProgram( program ); + this.program = undefined; - this.map = source.map; + }; - this.alphaMap = source.alphaMap; + // - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.name = parameters.shaderName; + this.id = programIdCount ++; + this.cacheKey = cacheKey; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + return this; - return this; +} - } +let _id = 0; -} +class WebGLShaderCache { -MeshDepthMaterial.prototype.isMeshDepthMaterial = true; + constructor() { -/** - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ + this.shaderCache = new Map(); + this.materialCache = new Map(); -class MeshDistanceMaterial extends Material { + } - constructor( parameters ) { + update( material ) { - super(); + const vertexShader = material.vertexShader; + const fragmentShader = material.fragmentShader; - this.type = 'MeshDistanceMaterial'; + const vertexShaderStage = this._getShaderStage( vertexShader ); + const fragmentShaderStage = this._getShaderStage( fragmentShader ); - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; + const materialShaders = this._getShaderCacheForMaterial( material ); - this.skinning = false; - this.morphTargets = false; + if ( materialShaders.has( vertexShaderStage ) === false ) { - this.map = null; + materialShaders.add( vertexShaderStage ); + vertexShaderStage.usedTimes ++; - this.alphaMap = null; + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( materialShaders.has( fragmentShaderStage ) === false ) { - this.fog = false; + materialShaders.add( fragmentShaderStage ); + fragmentShaderStage.usedTimes ++; - this.setValues( parameters ); + } + + return this; } - copy( source ) { + remove( material ) { - super.copy( source ); + const materialShaders = this.materialCache.get( material ); - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; + for ( const shaderStage of materialShaders ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + shaderStage.usedTimes --; - this.map = source.map; + if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); - this.alphaMap = source.alphaMap; + } - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.materialCache.delete( material ); return this; } -} - -MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - -var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; + getVertexShaderID( material ) { -var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; + return this._getShaderStage( material.vertexShader ).id; -function WebGLShadowMap( _renderer, _objects, _capabilities ) { + } - let _frustum = new Frustum(); + getFragmentShaderID( material ) { - const _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), + return this._getShaderStage( material.fragmentShader ).id; - _viewport = new Vector4(), + } - _depthMaterials = [], - _distanceMaterials = [], + dispose() { - _materialCache = {}, + this.shaderCache.clear(); + this.materialCache.clear(); - _maxTextureSize = _capabilities.maxTextureSize; + } - const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; + _getShaderCacheForMaterial( material ) { - const shadowMaterialVertical = new ShaderMaterial( { + const cache = this.materialCache; - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, + if ( cache.has( material ) === false ) { - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, + cache.set( material, new Set() ); - vertexShader: vsm_vert, + } - fragmentShader: vsm_frag + return cache.get( material ); - } ); + } - const shadowMaterialHorizontal = shadowMaterialVertical.clone(); - shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; + _getShaderStage( code ) { - const fullScreenTri = new BufferGeometry(); - fullScreenTri.setAttribute( - 'position', - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); + const cache = this.shaderCache; - const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); + if ( cache.has( code ) === false ) { - const scope = this; + const stage = new WebGLShaderStage( code ); + cache.set( code, stage ); - this.enabled = false; + } - this.autoUpdate = true; - this.needsUpdate = false; + return cache.get( code ); - this.type = PCFShadowMap; + } - this.render = function ( lights, scene, camera ) { +} - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; +class WebGLShaderStage { - if ( lights.length === 0 ) return; + constructor( code ) { - const currentRenderTarget = _renderer.getRenderTarget(); - const activeCubeFace = _renderer.getActiveCubeFace(); - const activeMipmapLevel = _renderer.getActiveMipmapLevel(); + this.id = _id ++; - const _state = _renderer.state; + this.code = code; + this.usedTimes = 0; - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); + } - // render depth map +} - for ( let i = 0, il = lights.length; i < il; i ++ ) { +function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { - const light = lights[ i ]; - const shadow = light.shadow; + const _programLayers = new Layers(); + const _customShaders = new WebGLShaderCache(); + const programs = []; - if ( shadow === undefined ) { + const isWebGL2 = capabilities.isWebGL2; + const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; + const floatVertexTextures = capabilities.floatVertexTextures; + const maxVertexUniforms = capabilities.maxVertexUniforms; + const vertexTextures = capabilities.vertexTextures; + let precision = capabilities.precision; - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; + const shaderIDs = { + MeshDepthMaterial: 'depth', + MeshDistanceMaterial: 'distanceRGBA', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshToonMaterial: 'toon', + MeshStandardMaterial: 'physical', + MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points', + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' + }; - } + function getMaxBones( object ) { - if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; + const skeleton = object.skeleton; + const bones = skeleton.bones; - _shadowMapSize.copy( shadow.mapSize ); + if ( floatVertexTextures ) { - const shadowFrameExtents = shadow.getFrameExtents(); + return 1024; - _shadowMapSize.multiply( shadowFrameExtents ); + } else { - _viewportSize.copy( shadow.mapSize ); + // default for when object is not specified + // ( for example when prebuilding shader to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) - if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { + const nVertexUniforms = maxVertexUniforms; + const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - if ( _shadowMapSize.x > _maxTextureSize ) { + const maxBones = Math.min( nVertexMatrices, bones.length ); - _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; + if ( maxBones < bones.length ) { - } + console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); + return 0; - if ( _shadowMapSize.y > _maxTextureSize ) { + } - _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; + return maxBones; - } + } - } + } - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + function getParameters( material, lights, shadows, scene, object ) { - const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; + const fog = scene.fog; + const geometry = object.geometry; + const environment = material.isMeshStandardMaterial ? scene.environment : null; - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + const shaderID = shaderIDs[ material.type ]; - shadow.camera.updateProjectionMatrix(); + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) - } + const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0; - if ( shadow.map === null ) { + if ( material.precision !== null ) { - const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; + precision = capabilities.getMaxPrecision( material.precision ); - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + if ( precision !== material.precision ) { - shadow.camera.updateProjectionMatrix(); + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - - const viewportCount = shadow.getViewportCount(); + } - for ( let vp = 0; vp < viewportCount; vp ++ ) { + // - const viewport = shadow.getViewport( vp ); + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); + let morphTextureStride = 0; - _state.viewport( _viewport ); + if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; + if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; + if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; - shadow.updateMatrices( light, vp ); + // - _frustum = shadow.getFrustum(); + let vertexShader, fragmentShader; + let customVertexShaderID, customFragmentShaderID; - renderObject( scene, camera, shadow.camera, light, this.type ); + if ( shaderID ) { - } + const shader = ShaderLib[ shaderID ]; - // do blur pass for VSM + vertexShader = shader.vertexShader; + fragmentShader = shader.fragmentShader; - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + } else { - VSMPass( shadow, camera ); + vertexShader = material.vertexShader; + fragmentShader = material.fragmentShader; - } + _customShaders.update( material ); - shadow.needsUpdate = false; + customVertexShaderID = _customShaders.getVertexShaderID( material ); + customFragmentShaderID = _customShaders.getFragmentShaderID( material ); } - scope.needsUpdate = false; - - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); + const currentRenderTarget = renderer.getRenderTarget(); - }; + const useAlphaTest = material.alphaTest > 0; + const useClearcoat = material.clearcoat > 0; - function VSMPass( shadow, camera ) { + const parameters = { - const geometry = _objects.update( fullScreenMesh ); + isWebGL2: isWebGL2, - // vertical pass + shaderID: shaderID, + shaderName: material.type, - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); + vertexShader: vertexShader, + fragmentShader: fragmentShader, + defines: material.defines, - // horizontal pass + customVertexShaderID: customVertexShaderID, + customFragmentShaderID: customFragmentShaderID, - shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); + isRawShaderMaterial: material.isRawShaderMaterial === true, + glslVersion: material.glslVersion, - } + precision: precision, - function getDepthMaterialVariant( useMorphing, useSkinning, useInstancing ) { + instancing: object.isInstancedMesh === true, + instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + supportsVertexTextures: vertexTextures, + outputEncoding: ( currentRenderTarget === null ) ? renderer.outputEncoding : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.encoding : LinearEncoding ), + map: !! material.map, + matcap: !! material.matcap, + envMap: !! envMap, + envMapMode: envMap && envMap.mapping, + envMapCubeUVHeight: envMapCubeUVHeight, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, + tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - let material = _depthMaterials[ index ]; + decodeVideoTexture: !! material.map && ( material.map.isVideoTexture === true ) && ( material.map.encoding === sRGBEncoding ), - if ( material === undefined ) { + clearcoat: useClearcoat, + clearcoatMap: useClearcoat && !! material.clearcoatMap, + clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, + clearcoatNormalMap: useClearcoat && !! material.clearcoatNormalMap, - material = new MeshDepthMaterial( { + displacementMap: !! material.displacementMap, + roughnessMap: !! material.roughnessMap, + metalnessMap: !! material.metalnessMap, + specularMap: !! material.specularMap, + specularIntensityMap: !! material.specularIntensityMap, + specularColorMap: !! material.specularColorMap, - depthPacking: RGBADepthPacking, + opaque: material.transparent === false && material.blending === NormalBlending, - morphTargets: useMorphing, - skinning: useSkinning + alphaMap: !! material.alphaMap, + alphaTest: useAlphaTest, - } ); + gradientMap: !! material.gradientMap, - _depthMaterials[ index ] = material; + sheen: material.sheen > 0, + sheenColorMap: !! material.sheenColorMap, + sheenRoughnessMap: !! material.sheenRoughnessMap, - } + transmission: material.transmission > 0, + transmissionMap: !! material.transmissionMap, + thicknessMap: !! material.thicknessMap, - return material; + combine: material.combine, - } + vertexTangents: ( !! material.normalMap && !! geometry.attributes.tangent ), + vertexColors: material.vertexColors, + vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, + vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || !! material.sheenColorMap || !! material.sheenRoughnessMap, + uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || material.transmission > 0 || !! material.transmissionMap || !! material.thicknessMap || !! material.specularIntensityMap || !! material.specularColorMap || material.sheen > 0 || !! material.sheenColorMap || !! material.sheenRoughnessMap ) && !! material.displacementMap, - function getDistanceMaterialVariant( useMorphing, useSkinning, useInstancing ) { + fog: !! fog, + useFog: material.fog, + fogExp2: ( fog && fog.isFogExp2 ), - const index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2; + flatShading: !! material.flatShading, - let material = _distanceMaterials[ index ]; + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: logarithmicDepthBuffer, - if ( material === undefined ) { + skinning: object.isSkinnedMesh === true && maxBones > 0, + maxBones: maxBones, + useVertexTexture: floatVertexTextures, - material = new MeshDistanceMaterial( { + morphTargets: geometry.morphAttributes.position !== undefined, + morphNormals: geometry.morphAttributes.normal !== undefined, + morphColors: geometry.morphAttributes.color !== undefined, + morphTargetsCount: morphTargetsCount, + morphTextureStride: morphTextureStride, - morphTargets: useMorphing, - skinning: useSkinning + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numRectAreaLights: lights.rectArea.length, + numHemiLights: lights.hemi.length, - } ); - - _distanceMaterials[ index ] = material; - - } - - return material; - - } - - function getDepthMaterial( object, geometry, material, light, shadowCameraNear, shadowCameraFar, type ) { - - let result = null; - - let getMaterialVariant = getDepthMaterialVariant; - let customMaterial = object.customDepthMaterial; + numDirLightShadows: lights.directionalShadowMap.length, + numPointLightShadows: lights.pointShadowMap.length, + numSpotLightShadows: lights.spotShadowMap.length, - if ( light.isPointLight === true ) { + numClippingPlanes: clipping.numPlanes, + numClipIntersection: clipping.numIntersection, - getMaterialVariant = getDistanceMaterialVariant; - customMaterial = object.customDistanceMaterial; + dithering: material.dithering, - } + shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, + shadowMapType: renderer.shadowMap.type, - if ( customMaterial === undefined ) { + toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, + physicallyCorrectLights: renderer.physicallyCorrectLights, - let useMorphing = false; + premultipliedAlpha: material.premultipliedAlpha, - if ( material.morphTargets === true ) { + doubleSided: material.side === DoubleSide, + flipSided: material.side === BackSide, - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; + depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false, - } + index0AttributeName: material.index0AttributeName, - let useSkinning = false; + extensionDerivatives: material.extensions && material.extensions.derivatives, + extensionFragDepth: material.extensions && material.extensions.fragDepth, + extensionDrawBuffers: material.extensions && material.extensions.drawBuffers, + extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD, - if ( object.isSkinnedMesh === true ) { + rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ), + rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ), + rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ), - if ( material.skinning === true ) { + customProgramCacheKey: material.customProgramCacheKey() - useSkinning = true; + }; - } else { + return parameters; - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); + } - } + function getProgramCacheKey( parameters ) { - } + const array = []; - const useInstancing = object.isInstancedMesh === true; + if ( parameters.shaderID ) { - result = getMaterialVariant( useMorphing, useSkinning, useInstancing ); + array.push( parameters.shaderID ); } else { - result = customMaterial; + array.push( parameters.customVertexShaderID ); + array.push( parameters.customFragmentShaderID ); } - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { - - // in this case we need a unique material instance reflecting the - // appropriate state - - const keyA = result.uuid, keyB = material.uuid; - - let materialsForVariant = _materialCache[ keyA ]; + if ( parameters.defines !== undefined ) { - if ( materialsForVariant === undefined ) { + for ( const name in parameters.defines ) { - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; + array.push( name ); + array.push( parameters.defines[ name ] ); } - let cachedMaterial = materialsForVariant[ keyB ]; - - if ( cachedMaterial === undefined ) { - - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; + } - } + if ( parameters.isRawShaderMaterial === false ) { - result = cachedMaterial; + getProgramCacheKeyParameters( array, parameters ); + getProgramCacheKeyBooleans( array, parameters ); + array.push( renderer.outputEncoding ); } - result.visible = material.visible; - result.wireframe = material.wireframe; + array.push( parameters.customProgramCacheKey ); - if ( type === VSMShadowMap ) { + return array.join(); - result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; + } - } else { + function getProgramCacheKeyParameters( array, parameters ) { + + array.push( parameters.precision ); + array.push( parameters.outputEncoding ); + array.push( parameters.envMapMode ); + array.push( parameters.envMapCubeUVHeight ); + array.push( parameters.combine ); + array.push( parameters.vertexUvs ); + array.push( parameters.fogExp2 ); + array.push( parameters.sizeAttenuation ); + array.push( parameters.maxBones ); + array.push( parameters.morphTargetsCount ); + array.push( parameters.morphAttributeCount ); + array.push( parameters.numDirLights ); + array.push( parameters.numPointLights ); + array.push( parameters.numSpotLights ); + array.push( parameters.numHemiLights ); + array.push( parameters.numRectAreaLights ); + array.push( parameters.numDirLightShadows ); + array.push( parameters.numPointLightShadows ); + array.push( parameters.numSpotLightShadows ); + array.push( parameters.shadowMapType ); + array.push( parameters.toneMapping ); + array.push( parameters.numClippingPlanes ); + array.push( parameters.numClipIntersection ); + + } + + function getProgramCacheKeyBooleans( array, parameters ) { + + _programLayers.disableAll(); + + if ( parameters.isWebGL2 ) + _programLayers.enable( 0 ); + if ( parameters.supportsVertexTextures ) + _programLayers.enable( 1 ); + if ( parameters.instancing ) + _programLayers.enable( 2 ); + if ( parameters.instancingColor ) + _programLayers.enable( 3 ); + if ( parameters.map ) + _programLayers.enable( 4 ); + if ( parameters.matcap ) + _programLayers.enable( 5 ); + if ( parameters.envMap ) + _programLayers.enable( 6 ); + if ( parameters.lightMap ) + _programLayers.enable( 7 ); + if ( parameters.aoMap ) + _programLayers.enable( 8 ); + if ( parameters.emissiveMap ) + _programLayers.enable( 9 ); + if ( parameters.bumpMap ) + _programLayers.enable( 10 ); + if ( parameters.normalMap ) + _programLayers.enable( 11 ); + if ( parameters.objectSpaceNormalMap ) + _programLayers.enable( 12 ); + if ( parameters.tangentSpaceNormalMap ) + _programLayers.enable( 13 ); + if ( parameters.clearcoat ) + _programLayers.enable( 14 ); + if ( parameters.clearcoatMap ) + _programLayers.enable( 15 ); + if ( parameters.clearcoatRoughnessMap ) + _programLayers.enable( 16 ); + if ( parameters.clearcoatNormalMap ) + _programLayers.enable( 17 ); + if ( parameters.displacementMap ) + _programLayers.enable( 18 ); + if ( parameters.specularMap ) + _programLayers.enable( 19 ); + if ( parameters.roughnessMap ) + _programLayers.enable( 20 ); + if ( parameters.metalnessMap ) + _programLayers.enable( 21 ); + if ( parameters.gradientMap ) + _programLayers.enable( 22 ); + if ( parameters.alphaMap ) + _programLayers.enable( 23 ); + if ( parameters.alphaTest ) + _programLayers.enable( 24 ); + if ( parameters.vertexColors ) + _programLayers.enable( 25 ); + if ( parameters.vertexAlphas ) + _programLayers.enable( 26 ); + if ( parameters.vertexUvs ) + _programLayers.enable( 27 ); + if ( parameters.vertexTangents ) + _programLayers.enable( 28 ); + if ( parameters.uvsVertexOnly ) + _programLayers.enable( 29 ); + if ( parameters.fog ) + _programLayers.enable( 30 ); + + array.push( _programLayers.mask ); + _programLayers.disableAll(); + + if ( parameters.useFog ) + _programLayers.enable( 0 ); + if ( parameters.flatShading ) + _programLayers.enable( 1 ); + if ( parameters.logarithmicDepthBuffer ) + _programLayers.enable( 2 ); + if ( parameters.skinning ) + _programLayers.enable( 3 ); + if ( parameters.useVertexTexture ) + _programLayers.enable( 4 ); + if ( parameters.morphTargets ) + _programLayers.enable( 5 ); + if ( parameters.morphNormals ) + _programLayers.enable( 6 ); + if ( parameters.morphColors ) + _programLayers.enable( 7 ); + if ( parameters.premultipliedAlpha ) + _programLayers.enable( 8 ); + if ( parameters.shadowMapEnabled ) + _programLayers.enable( 9 ); + if ( parameters.physicallyCorrectLights ) + _programLayers.enable( 10 ); + if ( parameters.doubleSided ) + _programLayers.enable( 11 ); + if ( parameters.flipSided ) + _programLayers.enable( 12 ); + if ( parameters.depthPacking ) + _programLayers.enable( 13 ); + if ( parameters.dithering ) + _programLayers.enable( 14 ); + if ( parameters.specularIntensityMap ) + _programLayers.enable( 15 ); + if ( parameters.specularColorMap ) + _programLayers.enable( 16 ); + if ( parameters.transmission ) + _programLayers.enable( 17 ); + if ( parameters.transmissionMap ) + _programLayers.enable( 18 ); + if ( parameters.thicknessMap ) + _programLayers.enable( 19 ); + if ( parameters.sheen ) + _programLayers.enable( 20 ); + if ( parameters.sheenColorMap ) + _programLayers.enable( 21 ); + if ( parameters.sheenRoughnessMap ) + _programLayers.enable( 22 ); + if ( parameters.decodeVideoTexture ) + _programLayers.enable( 23 ); + if ( parameters.opaque ) + _programLayers.enable( 24 ); + + array.push( _programLayers.mask ); - result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; + } - } + function getUniforms( material ) { - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; + const shaderID = shaderIDs[ material.type ]; + let uniforms; - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; + if ( shaderID ) { - if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { + const shader = ShaderLib[ shaderID ]; + uniforms = UniformsUtils.clone( shader.uniforms ); - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; + } else { + + uniforms = material.uniforms; } - return result; + return uniforms; } - function renderObject( object, camera, shadowCamera, light, type ) { - - if ( object.visible === false ) return; - - const visible = object.layers.test( camera.layers ); - - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { + function acquireProgram( parameters, cacheKey ) { - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { + let program; - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + // Check if code has been already compiled + for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - const geometry = _objects.update( object ); - const material = object.material; + const preexistingProgram = programs[ p ]; - if ( Array.isArray( material ) ) { + if ( preexistingProgram.cacheKey === cacheKey ) { - const groups = geometry.groups; + program = preexistingProgram; + ++ program.usedTimes; - for ( let k = 0, kl = groups.length; k < kl; k ++ ) { + break; - const group = groups[ k ]; - const groupMaterial = material[ group.materialIndex ]; + } - if ( groupMaterial && groupMaterial.visible ) { + } - const depthMaterial = getDepthMaterial( object, geometry, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); + if ( program === undefined ) { - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); + program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); + programs.push( program ); - } + } - } + return program; - } else if ( material.visible ) { + } - const depthMaterial = getDepthMaterial( object, geometry, material, light, shadowCamera.near, shadowCamera.far, type ); + function releaseProgram( program ) { - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); + if ( -- program.usedTimes === 0 ) { - } + // Remove from unordered set + const i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); - } + // Free WebGL resources + program.destroy(); } - const children = object.children; - - for ( let i = 0, l = children.length; i < l; i ++ ) { + } - renderObject( children[ i ], camera, shadowCamera, light, type ); + function releaseShaderCache( material ) { - } + _customShaders.remove( material ); } -} - -function WebGLState( gl, extensions, capabilities ) { + function dispose() { - const isWebGL2 = capabilities.isWebGL2; + _customShaders.dispose(); - function ColorBuffer() { + } - let locked = false; + return { + getParameters: getParameters, + getProgramCacheKey: getProgramCacheKey, + getUniforms: getUniforms, + acquireProgram: acquireProgram, + releaseProgram: releaseProgram, + releaseShaderCache: releaseShaderCache, + // Exposed for resource monitoring & error feedback via renderer.info: + programs: programs, + dispose: dispose + }; - const color = new Vector4(); - let currentColorMask = null; - const currentColorClear = new Vector4( 0, 0, 0, 0 ); +} - return { +function WebGLProperties() { - setMask: function ( colorMask ) { + let properties = new WeakMap(); - if ( currentColorMask !== colorMask && ! locked ) { + function get( object ) { - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; + let map = properties.get( object ); - } + if ( map === undefined ) { - }, + map = {}; + properties.set( object, map ); - setLocked: function ( lock ) { + } - locked = lock; + return map; - }, + } - setClear: function ( r, g, b, a, premultipliedAlpha ) { + function remove( object ) { - if ( premultipliedAlpha === true ) { + properties.delete( object ); - r *= a; g *= a; b *= a; + } - } + function update( object, key, value ) { - color.set( r, g, b, a ); + properties.get( object )[ key ] = value; - if ( currentColorClear.equals( color ) === false ) { + } - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); + function dispose() { - } + properties = new WeakMap(); - }, + } - reset: function () { + return { + get: get, + remove: remove, + update: update, + dispose: dispose + }; - locked = false; +} - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state +function painterSortStable( a, b ) { - } + if ( a.groupOrder !== b.groupOrder ) { - }; + return a.groupOrder - b.groupOrder; - } + } else if ( a.renderOrder !== b.renderOrder ) { - function DepthBuffer() { + return a.renderOrder - b.renderOrder; - let locked = false; + } else if ( a.material.id !== b.material.id ) { - let currentDepthMask = null; - let currentDepthFunc = null; - let currentDepthClear = null; + return a.material.id - b.material.id; - return { + } else if ( a.z !== b.z ) { - setTest: function ( depthTest ) { + return a.z - b.z; - if ( depthTest ) { + } else { - enable( 2929 ); + return a.id - b.id; - } else { + } - disable( 2929 ); +} - } +function reversePainterSortStable( a, b ) { - }, + if ( a.groupOrder !== b.groupOrder ) { - setMask: function ( depthMask ) { + return a.groupOrder - b.groupOrder; - if ( currentDepthMask !== depthMask && ! locked ) { + } else if ( a.renderOrder !== b.renderOrder ) { - gl.depthMask( depthMask ); - currentDepthMask = depthMask; + return a.renderOrder - b.renderOrder; - } + } else if ( a.z !== b.z ) { - }, + return b.z - a.z; - setFunc: function ( depthFunc ) { + } else { - if ( currentDepthFunc !== depthFunc ) { + return a.id - b.id; - if ( depthFunc ) { + } - switch ( depthFunc ) { +} - case NeverDepth: - gl.depthFunc( 512 ); - break; +function WebGLRenderList() { - case AlwaysDepth: + const renderItems = []; + let renderItemsIndex = 0; - gl.depthFunc( 519 ); - break; + const opaque = []; + const transmissive = []; + const transparent = []; - case LessDepth: + function init() { - gl.depthFunc( 513 ); - break; + renderItemsIndex = 0; - case LessEqualDepth: + opaque.length = 0; + transmissive.length = 0; + transparent.length = 0; - gl.depthFunc( 515 ); - break; + } - case EqualDepth: + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - gl.depthFunc( 514 ); - break; + let renderItem = renderItems[ renderItemsIndex ]; - case GreaterEqualDepth: + if ( renderItem === undefined ) { - gl.depthFunc( 518 ); - break; + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + groupOrder: groupOrder, + renderOrder: object.renderOrder, + z: z, + group: group + }; - case GreaterDepth: + renderItems[ renderItemsIndex ] = renderItem; - gl.depthFunc( 516 ); - break; + } else { - case NotEqualDepth: + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.groupOrder = groupOrder; + renderItem.renderOrder = object.renderOrder; + renderItem.z = z; + renderItem.group = group; - gl.depthFunc( 517 ); - break; + } - default: + renderItemsIndex ++; - gl.depthFunc( 515 ); + return renderItem; - } + } - } else { + function push( object, geometry, material, groupOrder, z, group ) { - gl.depthFunc( 515 ); + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - } + if ( material.transmission > 0.0 ) { - currentDepthFunc = depthFunc; + transmissive.push( renderItem ); - } + } else if ( material.transparent === true ) { - }, + transparent.push( renderItem ); - setLocked: function ( lock ) { + } else { - locked = lock; + opaque.push( renderItem ); - }, + } - setClear: function ( depth ) { + } - if ( currentDepthClear !== depth ) { + function unshift( object, geometry, material, groupOrder, z, group ) { - gl.clearDepth( depth ); - currentDepthClear = depth; + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - } + if ( material.transmission > 0.0 ) { - }, + transmissive.unshift( renderItem ); - reset: function () { + } else if ( material.transparent === true ) { - locked = false; + transparent.unshift( renderItem ); - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; + } else { - } + opaque.unshift( renderItem ); - }; + } } - function StencilBuffer() { - - let locked = false; - - let currentStencilMask = null; - let currentStencilFunc = null; - let currentStencilRef = null; - let currentStencilFuncMask = null; - let currentStencilFail = null; - let currentStencilZFail = null; - let currentStencilZPass = null; - let currentStencilClear = null; + function sort( customOpaqueSort, customTransparentSort ) { - return { + if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); + if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); + if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - setTest: function ( stencilTest ) { + } - if ( ! locked ) { + function finish() { - if ( stencilTest ) { + // Clear references from inactive renderItems in the list - enable( 2960 ); + for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - } else { + const renderItem = renderItems[ i ]; - disable( 2960 ); + if ( renderItem.id === null ) break; - } + renderItem.id = null; + renderItem.object = null; + renderItem.geometry = null; + renderItem.material = null; + renderItem.group = null; - } + } - }, + } - setMask: function ( stencilMask ) { + return { - if ( currentStencilMask !== stencilMask && ! locked ) { + opaque: opaque, + transmissive: transmissive, + transparent: transparent, - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; + init: init, + push: push, + unshift: unshift, + finish: finish, - } + sort: sort + }; - }, +} - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { +function WebGLRenderLists() { - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { + let lists = new WeakMap(); - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); + function get( scene, renderCallDepth ) { - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; + let list; - } + if ( lists.has( scene ) === false ) { - }, + list = new WebGLRenderList(); + lists.set( scene, [ list ] ); - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { + } else { - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { + if ( renderCallDepth >= lists.get( scene ).length ) { - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); + list = new WebGLRenderList(); + lists.get( scene ).push( list ); - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; + } else { - } + list = lists.get( scene )[ renderCallDepth ]; - }, + } - setLocked: function ( lock ) { + } - locked = lock; + return list; - }, + } - setClear: function ( stencil ) { + function dispose() { - if ( currentStencilClear !== stencil ) { + lists = new WeakMap(); - gl.clearStencil( stencil ); - currentStencilClear = stencil; + } - } + return { + get: get, + dispose: dispose + }; - }, +} - reset: function () { +function UniformsCache() { - locked = false; + const lights = {}; - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; + return { - } + get: function ( light ) { - }; + if ( lights[ light.id ] !== undefined ) { - } + return lights[ light.id ]; - // + } - const colorBuffer = new ColorBuffer(); - const depthBuffer = new DepthBuffer(); - const stencilBuffer = new StencilBuffer(); + let uniforms; - let enabledCapabilities = {}; + switch ( light.type ) { - let xrFramebuffer = null; - let currentBoundFramebuffers = {}; + case 'DirectionalLight': + uniforms = { + direction: new Vector3(), + color: new Color() + }; + break; - let currentProgram = null; - - let currentBlendingEnabled = false; - let currentBlending = null; - let currentBlendEquation = null; - let currentBlendSrc = null; - let currentBlendDst = null; - let currentBlendEquationAlpha = null; - let currentBlendSrcAlpha = null; - let currentBlendDstAlpha = null; - let currentPremultipledAlpha = false; + case 'SpotLight': + uniforms = { + position: new Vector3(), + direction: new Vector3(), + color: new Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0 + }; + break; - let currentFlipSided = null; - let currentCullFace = null; + case 'PointLight': + uniforms = { + position: new Vector3(), + color: new Color(), + distance: 0, + decay: 0 + }; + break; - let currentLineWidth = null; + case 'HemisphereLight': + uniforms = { + direction: new Vector3(), + skyColor: new Color(), + groundColor: new Color() + }; + break; - let currentPolygonOffsetFactor = null; - let currentPolygonOffsetUnits = null; + case 'RectAreaLight': + uniforms = { + color: new Color(), + position: new Vector3(), + halfWidth: new Vector3(), + halfHeight: new Vector3() + }; + break; - const maxTextures = gl.getParameter( 35661 ); + } - let lineWidthAvailable = false; - let version = 0; - const glVersion = gl.getParameter( 7938 ); + lights[ light.id ] = uniforms; - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { + return uniforms; - version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); + } - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { + }; - version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); +} - } +function ShadowUniformsCache() { - let currentTextureSlot = null; - let currentBoundTextures = {}; + const lights = {}; - const currentScissor = new Vector4( 0, 0, gl.canvas.width, gl.canvas.height ); - const currentViewport = new Vector4( 0, 0, gl.canvas.width, gl.canvas.height ); + return { - function createTexture( type, target, count ) { + get: function ( light ) { - const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - const texture = gl.createTexture(); + if ( lights[ light.id ] !== undefined ) { - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); + return lights[ light.id ]; - for ( let i = 0; i < count; i ++ ) { + } - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); + let uniforms; - } + switch ( light.type ) { - return texture; + case 'DirectionalLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - } + case 'SpotLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - const emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); + case 'PointLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2(), + shadowCameraNear: 1, + shadowCameraFar: 1000 + }; + break; - // init + // TODO (abelnation): set RectAreaLight shadow uniforms - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); + } - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); + lights[ light.id ] = uniforms; - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); + return uniforms; - setBlending( NoBlending ); + } - // + }; - function enable( id ) { +} - if ( enabledCapabilities[ id ] !== true ) { - gl.enable( id ); - enabledCapabilities[ id ] = true; - } +let nextVersion = 0; - } +function shadowCastingLightsFirst( lightA, lightB ) { - function disable( id ) { + return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); - if ( enabledCapabilities[ id ] !== false ) { +} - gl.disable( id ); - enabledCapabilities[ id ] = false; +function WebGLLights( extensions, capabilities ) { - } + const cache = new UniformsCache(); - } + const shadowCache = ShadowUniformsCache(); - function bindXRFramebuffer( framebuffer ) { + const state = { - if ( framebuffer !== xrFramebuffer ) { + version: 0, - gl.bindFramebuffer( 36160, framebuffer ); + hash: { + directionalLength: - 1, + pointLength: - 1, + spotLength: - 1, + rectAreaLength: - 1, + hemiLength: - 1, - xrFramebuffer = framebuffer; + numDirectionalShadows: - 1, + numPointShadows: - 1, + numSpotShadows: - 1 + }, - } + ambient: [ 0, 0, 0 ], + probe: [], + directional: [], + directionalShadow: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadow: [], + spotShadowMap: [], + spotShadowMatrix: [], + rectArea: [], + rectAreaLTC1: null, + rectAreaLTC2: null, + point: [], + pointShadow: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [] - } + }; - function bindFramebuffer( target, framebuffer ) { + for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - if ( framebuffer === null && xrFramebuffer !== null ) framebuffer = xrFramebuffer; // use active XR framebuffer if available + const vector3 = new Vector3(); + const matrix4 = new Matrix4(); + const matrix42 = new Matrix4(); - if ( currentBoundFramebuffers[ target ] !== framebuffer ) { + function setup( lights, physicallyCorrectLights ) { - gl.bindFramebuffer( target, framebuffer ); + let r = 0, g = 0, b = 0; - currentBoundFramebuffers[ target ] = framebuffer; + for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - if ( isWebGL2 ) { + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - // 36009 is equivalent to 36160 + let numDirectionalShadows = 0; + let numPointShadows = 0; + let numSpotShadows = 0; - if ( target === 36009 ) { + lights.sort( shadowCastingLightsFirst ); - currentBoundFramebuffers[ 36160 ] = framebuffer; + // artist-friendly light intensity scaling factor + const scaleFactor = ( physicallyCorrectLights !== true ) ? Math.PI : 1; - } + for ( let i = 0, l = lights.length; i < l; i ++ ) { - if ( target === 36160 ) { + const light = lights[ i ]; - currentBoundFramebuffers[ 36009 ] = framebuffer; + const color = light.color; + const intensity = light.intensity; + const distance = light.distance; - } + const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - } + if ( light.isAmbientLight ) { - } + r += color.r * intensity * scaleFactor; + g += color.g * intensity * scaleFactor; + b += color.b * intensity * scaleFactor; - } + } else if ( light.isLightProbe ) { - function useProgram( program ) { + for ( let j = 0; j < 9; j ++ ) { - if ( currentProgram !== program ) { + state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); - gl.useProgram( program ); + } - currentProgram = program; + } else if ( light.isDirectionalLight ) { - return true; + const uniforms = cache.get( light ); - } + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - return false; + if ( light.castShadow ) { - } + const shadow = light.shadow; - const equationToGL = { - [ AddEquation ]: 32774, - [ SubtractEquation ]: 32778, - [ ReverseSubtractEquation ]: 32779 - }; + const shadowUniforms = shadowCache.get( light ); - if ( isWebGL2 ) { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - equationToGL[ MinEquation ] = 32775; - equationToGL[ MaxEquation ] = 32776; + state.directionalShadow[ directionalLength ] = shadowUniforms; + state.directionalShadowMap[ directionalLength ] = shadowMap; + state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - } else { + numDirectionalShadows ++; - const extension = extensions.get( 'EXT_blend_minmax' ); + } - if ( extension !== null ) { + state.directional[ directionalLength ] = uniforms; - equationToGL[ MinEquation ] = extension.MIN_EXT; - equationToGL[ MaxEquation ] = extension.MAX_EXT; + directionalLength ++; - } + } else if ( light.isSpotLight ) { - } + const uniforms = cache.get( light ); - const factorToGL = { - [ ZeroFactor ]: 0, - [ OneFactor ]: 1, - [ SrcColorFactor ]: 768, - [ SrcAlphaFactor ]: 770, - [ SrcAlphaSaturateFactor ]: 776, - [ DstColorFactor ]: 774, - [ DstAlphaFactor ]: 772, - [ OneMinusSrcColorFactor ]: 769, - [ OneMinusSrcAlphaFactor ]: 771, - [ OneMinusDstColorFactor ]: 775, - [ OneMinusDstAlphaFactor ]: 773 - }; + uniforms.position.setFromMatrixPosition( light.matrixWorld ); - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); + uniforms.distance = distance; - if ( blending === NoBlending ) { + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = light.decay; - if ( currentBlendingEnabled === true ) { + if ( light.castShadow ) { - disable( 3042 ); - currentBlendingEnabled = false; + const shadow = light.shadow; - } + const shadowUniforms = shadowCache.get( light ); - return; + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - } + state.spotShadow[ spotLength ] = shadowUniforms; + state.spotShadowMap[ spotLength ] = shadowMap; + state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; - if ( currentBlendingEnabled === false ) { + numSpotShadows ++; - enable( 3042 ); - currentBlendingEnabled = true; + } - } + state.spot[ spotLength ] = uniforms; - if ( blending !== CustomBlending ) { + spotLength ++; - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + } else if ( light.isRectAreaLight ) { - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { + const uniforms = cache.get( light ); - gl.blendEquation( 32774 ); + // (a) intensity is the total visible light emitted + //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; + // (b) intensity is the brightness of the light + uniforms.color.copy( color ).multiplyScalar( intensity ); - } + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - if ( premultipliedAlpha ) { + state.rectArea[ rectAreaLength ] = uniforms; - switch ( blending ) { + rectAreaLength ++; - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; + } else if ( light.isPointLight ) { - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; + const uniforms = cache.get( light ); - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); + uniforms.distance = light.distance; + uniforms.decay = light.decay; - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; + if ( light.castShadow ) { - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + const shadow = light.shadow; - } + const shadowUniforms = shadowCache.get( light ); - } else { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; + shadowUniforms.shadowCameraNear = shadow.camera.near; + shadowUniforms.shadowCameraFar = shadow.camera.far; - switch ( blending ) { + state.pointShadow[ pointLength ] = shadowUniforms; + state.pointShadowMap[ pointLength ] = shadowMap; + state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; + numPointShadows ++; - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; + } - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; + state.point[ pointLength ] = uniforms; - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; + pointLength ++; - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + } else if ( light.isHemisphereLight ) { - } + const uniforms = cache.get( light ); - } + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; + state.hemi[ hemiLength ] = uniforms; - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; + hemiLength ++; } - return; - } - // custom blending + if ( rectAreaLength > 0 ) { - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + if ( capabilities.isWebGL2 ) { - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + // WebGL 2 - gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + } else { - } + // WebGL 1 - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + if ( extensions.has( 'OES_texture_float_linear' ) === true ) { - gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { - } + state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; + state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; - currentBlending = blending; - currentPremultipledAlpha = null; + } else { - } + console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); - function setMaterial( material, frontFaceCW ) { + } - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); + } - let flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; + } - setFlipSided( flipSided ); + state.ambient[ 0 ] = r; + state.ambient[ 1 ] = g; + state.ambient[ 2 ] = b; - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + const hash = state.hash; - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); + if ( hash.directionalLength !== directionalLength || + hash.pointLength !== pointLength || + hash.spotLength !== spotLength || + hash.rectAreaLength !== rectAreaLength || + hash.hemiLength !== hemiLength || + hash.numDirectionalShadows !== numDirectionalShadows || + hash.numPointShadows !== numPointShadows || + hash.numSpotShadows !== numSpotShadows ) { - const stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; - stencilBuffer.setMask( material.stencilWriteMask ); - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); + state.directionalShadow.length = numDirectionalShadows; + state.directionalShadowMap.length = numDirectionalShadows; + state.pointShadow.length = numPointShadows; + state.pointShadowMap.length = numPointShadows; + state.spotShadow.length = numSpotShadows; + state.spotShadowMap.length = numSpotShadows; + state.directionalShadowMatrix.length = numDirectionalShadows; + state.pointShadowMatrix.length = numPointShadows; + state.spotShadowMatrix.length = numSpotShadows; - } + hash.directionalLength = directionalLength; + hash.pointLength = pointLength; + hash.spotLength = spotLength; + hash.rectAreaLength = rectAreaLength; + hash.hemiLength = hemiLength; - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + hash.numDirectionalShadows = numDirectionalShadows; + hash.numPointShadows = numPointShadows; + hash.numSpotShadows = numSpotShadows; - material.alphaToCoverage === true - ? enable( 32926 ) - : disable( 32926 ); + state.version = nextVersion ++; + + } } - // + function setupView( lights, camera ) { - function setFlipSided( flipSided ) { + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - if ( currentFlipSided !== flipSided ) { + const viewMatrix = camera.matrixWorldInverse; - if ( flipSided ) { + for ( let i = 0, l = lights.length; i < l; i ++ ) { - gl.frontFace( 2304 ); + const light = lights[ i ]; - } else { - - gl.frontFace( 2305 ); - - } + if ( light.isDirectionalLight ) { - currentFlipSided = flipSided; + const uniforms = state.directional[ directionalLength ]; - } + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - } + directionalLength ++; - function setCullFace( cullFace ) { + } else if ( light.isSpotLight ) { - if ( cullFace !== CullFaceNone ) { + const uniforms = state.spot[ spotLength ]; - enable( 2884 ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - if ( cullFace !== currentCullFace ) { + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - if ( cullFace === CullFaceBack ) { + spotLength ++; - gl.cullFace( 1029 ); + } else if ( light.isRectAreaLight ) { - } else if ( cullFace === CullFaceFront ) { + const uniforms = state.rectArea[ rectAreaLength ]; - gl.cullFace( 1028 ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } else { + // extract local rotation of light to derive width/height half vectors + matrix42.identity(); + matrix4.copy( light.matrixWorld ); + matrix4.premultiply( viewMatrix ); + matrix42.extractRotation( matrix4 ); - gl.cullFace( 1032 ); + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - } + uniforms.halfWidth.applyMatrix4( matrix42 ); + uniforms.halfHeight.applyMatrix4( matrix42 ); - } + rectAreaLength ++; - } else { + } else if ( light.isPointLight ) { - disable( 2884 ); + const uniforms = state.point[ pointLength ]; - } + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - currentCullFace = cullFace; + pointLength ++; - } + } else if ( light.isHemisphereLight ) { - function setLineWidth( width ) { + const uniforms = state.hemi[ hemiLength ]; - if ( width !== currentLineWidth ) { + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); - if ( lineWidthAvailable ) gl.lineWidth( width ); + hemiLength ++; - currentLineWidth = width; + } } } - function setPolygonOffset( polygonOffset, factor, units ) { + return { + setup: setup, + setupView: setupView, + state: state + }; - if ( polygonOffset ) { +} - enable( 32823 ); +function WebGLRenderState( extensions, capabilities ) { - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { + const lights = new WebGLLights( extensions, capabilities ); - gl.polygonOffset( factor, units ); + const lightsArray = []; + const shadowsArray = []; - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; + function init() { - } + lightsArray.length = 0; + shadowsArray.length = 0; - } else { + } - disable( 32823 ); + function pushLight( light ) { - } + lightsArray.push( light ); } - function setScissorTest( scissorTest ) { - - if ( scissorTest ) { + function pushShadow( shadowLight ) { - enable( 3089 ); + shadowsArray.push( shadowLight ); - } else { + } - disable( 3089 ); + function setupLights( physicallyCorrectLights ) { - } + lights.setup( lightsArray, physicallyCorrectLights ); } - // texture - - function activeTexture( webglSlot ) { + function setupLightsView( camera ) { - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; + lights.setupView( lightsArray, camera ); - if ( currentTextureSlot !== webglSlot ) { + } - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; + const state = { + lightsArray: lightsArray, + shadowsArray: shadowsArray, - } + lights: lights + }; - } + return { + init: init, + state: state, + setupLights: setupLights, + setupLightsView: setupLightsView, - function bindTexture( webglType, webglTexture ) { + pushLight: pushLight, + pushShadow: pushShadow + }; - if ( currentTextureSlot === null ) { +} - activeTexture(); +function WebGLRenderStates( extensions, capabilities ) { - } + let renderStates = new WeakMap(); - let boundTexture = currentBoundTextures[ currentTextureSlot ]; + function get( scene, renderCallDepth = 0 ) { - if ( boundTexture === undefined ) { + let renderState; - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; + if ( renderStates.has( scene ) === false ) { - } + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.set( scene, [ renderState ] ); - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + } else { - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); + if ( renderCallDepth >= renderStates.get( scene ).length ) { - boundTexture.type = webglType; - boundTexture.texture = webglTexture; + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.get( scene ).push( renderState ); - } + } else { - } + renderState = renderStates.get( scene )[ renderCallDepth ]; - function unbindTexture() { + } - const boundTexture = currentBoundTextures[ currentTextureSlot ]; + } - if ( boundTexture !== undefined && boundTexture.type !== undefined ) { + return renderState; - gl.bindTexture( boundTexture.type, null ); + } - boundTexture.type = undefined; - boundTexture.texture = undefined; + function dispose() { - } + renderStates = new WeakMap(); } - function compressedTexImage2D() { + return { + get: get, + dispose: dispose + }; - try { +} - gl.compressedTexImage2D.apply( gl, arguments ); +class MeshDepthMaterial extends Material { - } catch ( error ) { + constructor( parameters ) { - console.error( 'THREE.WebGLState:', error ); + super(); - } + this.type = 'MeshDepthMaterial'; - } + this.depthPacking = BasicDepthPacking; - function texImage2D() { + this.map = null; - try { + this.alphaMap = null; - gl.texImage2D.apply( gl, arguments ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } catch ( error ) { + this.wireframe = false; + this.wireframeLinewidth = 1; - console.error( 'THREE.WebGLState:', error ); + this.fog = false; - } + this.setValues( parameters ); } - function texImage3D() { + copy( source ) { - try { + super.copy( source ); - gl.texImage3D.apply( gl, arguments ); + this.depthPacking = source.depthPacking; - } catch ( error ) { + this.map = source.map; - console.error( 'THREE.WebGLState:', error ); + this.alphaMap = source.alphaMap; - } + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - // + return this; - function scissor( scissor ) { + } - if ( currentScissor.equals( scissor ) === false ) { +} - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); +MeshDepthMaterial.prototype.isMeshDepthMaterial = true; - } +class MeshDistanceMaterial extends Material { - } + constructor( parameters ) { - function viewport( viewport ) { + super(); - if ( currentViewport.equals( viewport ) === false ) { + this.type = 'MeshDistanceMaterial'; - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); + this.referencePosition = new Vector3(); + this.nearDistance = 1; + this.farDistance = 1000; - } + this.map = null; - } + this.alphaMap = null; - // + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - function reset() { + this.fog = false; - // reset state + this.setValues( parameters ); - gl.disable( 3042 ); - gl.disable( 2884 ); - gl.disable( 2929 ); - gl.disable( 32823 ); - gl.disable( 3089 ); - gl.disable( 2960 ); - gl.disable( 32926 ); + } - gl.blendEquation( 32774 ); - gl.blendFunc( 1, 0 ); - gl.blendFuncSeparate( 1, 0, 1, 0 ); + copy( source ) { - gl.colorMask( true, true, true, true ); - gl.clearColor( 0, 0, 0, 0 ); + super.copy( source ); - gl.depthMask( true ); - gl.depthFunc( 513 ); - gl.clearDepth( 1 ); + this.referencePosition.copy( source.referencePosition ); + this.nearDistance = source.nearDistance; + this.farDistance = source.farDistance; - gl.stencilMask( 0xffffffff ); - gl.stencilFunc( 519, 0, 0xffffffff ); - gl.stencilOp( 7680, 7680, 7680 ); - gl.clearStencil( 0 ); + this.map = source.map; - gl.cullFace( 1029 ); - gl.frontFace( 2305 ); + this.alphaMap = source.alphaMap; - gl.polygonOffset( 0, 0 ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - gl.activeTexture( 33984 ); + return this; - gl.bindFramebuffer( 36160, null ); + } - if ( isWebGL2 === true ) { +} - gl.bindFramebuffer( 36009, null ); - gl.bindFramebuffer( 36008, null ); +MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; - } +const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - gl.useProgram( null ); +const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - gl.lineWidth( 1 ); +function WebGLShadowMap( _renderer, _objects, _capabilities ) { - gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); - gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + let _frustum = new Frustum(); - // reset internals + const _shadowMapSize = new Vector2(), + _viewportSize = new Vector2(), - enabledCapabilities = {}; + _viewport = new Vector4(), - currentTextureSlot = null; - currentBoundTextures = {}; + _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), + _distanceMaterial = new MeshDistanceMaterial(), - xrFramebuffer = null; - currentBoundFramebuffers = {}; + _materialCache = {}, - currentProgram = null; + _maxTextureSize = _capabilities.maxTextureSize; - currentBlendingEnabled = false; - currentBlending = null; - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; - currentPremultipledAlpha = false; + const shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; - currentFlipSided = null; - currentCullFace = null; + const shadowMaterialVertical = new ShaderMaterial( { + defines: { + VSM_SAMPLES: 8 + }, + uniforms: { + shadow_pass: { value: null }, + resolution: { value: new Vector2() }, + radius: { value: 4.0 } + }, - currentLineWidth = null; + vertexShader: vertex, + fragmentShader: fragment - currentPolygonOffsetFactor = null; - currentPolygonOffsetUnits = null; + } ); - currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); - currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); + const shadowMaterialHorizontal = shadowMaterialVertical.clone(); + shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); + const fullScreenTri = new BufferGeometry(); + fullScreenTri.setAttribute( + 'position', + new BufferAttribute( + new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), + 3 + ) + ); - } + const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - return { + const scope = this; - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, + this.enabled = false; - enable: enable, - disable: disable, + this.autoUpdate = true; + this.needsUpdate = false; - bindFramebuffer: bindFramebuffer, - bindXRFramebuffer: bindXRFramebuffer, + this.type = PCFShadowMap; - useProgram: useProgram, + this.render = function ( lights, scene, camera ) { - setBlending: setBlending, - setMaterial: setMaterial, + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - setFlipSided: setFlipSided, - setCullFace: setCullFace, + if ( lights.length === 0 ) return; - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, + const currentRenderTarget = _renderer.getRenderTarget(); + const activeCubeFace = _renderer.getActiveCubeFace(); + const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - setScissorTest: setScissorTest, + const _state = _renderer.state; - activeTexture: activeTexture, - bindTexture: bindTexture, - unbindTexture: unbindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, + // Set GL state for depth map. + _state.setBlending( NoBlending ); + _state.buffers.color.setClear( 1, 1, 1, 1 ); + _state.buffers.depth.setTest( true ); + _state.setScissorTest( false ); - scissor: scissor, - viewport: viewport, + // render depth map - reset: reset + for ( let i = 0, il = lights.length; i < il; i ++ ) { - }; + const light = lights[ i ]; + const shadow = light.shadow; -} + if ( shadow === undefined ) { -function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { + console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + continue; - const isWebGL2 = capabilities.isWebGL2; - const maxTextures = capabilities.maxTextures; - const maxCubemapSize = capabilities.maxCubemapSize; - const maxTextureSize = capabilities.maxTextureSize; - const maxSamples = capabilities.maxSamples; + } - const _videoTextures = new WeakMap(); - let _canvas; + if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, - // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! - // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). + _shadowMapSize.copy( shadow.mapSize ); - let useOffscreenCanvas = false; + const shadowFrameExtents = shadow.getFrameExtents(); - try { + _shadowMapSize.multiply( shadowFrameExtents ); - useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; + _viewportSize.copy( shadow.mapSize ); - } catch ( err ) { + if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { - // Ignore any errors + if ( _shadowMapSize.x > _maxTextureSize ) { - } + _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); + _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; + shadow.mapSize.x = _viewportSize.x; - function createCanvas( width, height ) { + } - // Use OffscreenCanvas when available. Specially needed in web workers + if ( _shadowMapSize.y > _maxTextureSize ) { - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); + _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; + shadow.mapSize.y = _viewportSize.y; - } + } - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { + } - let scale = 1; + if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - // handle case if texture exceeds max size + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); + shadow.map.texture.name = light.name + '.shadowMap'; - if ( image.width > maxSize || image.height > maxSize ) { + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); - scale = maxSize / Math.max( image.width, image.height ); + shadow.camera.updateProjectionMatrix(); - } + } - // only perform resize if necessary + if ( shadow.map === null ) { - if ( scale < 1 || needsPowerOfTwo === true ) { + const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; - // only perform resize for certain image types + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + '.shadowMap'; - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { + shadow.camera.updateProjectionMatrix(); - const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; + } - const width = floor( scale * image.width ); - const height = floor( scale * image.height ); + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); + const viewportCount = shadow.getViewportCount(); - // cube textures can't reuse the same canvas + for ( let vp = 0; vp < viewportCount; vp ++ ) { - const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; + const viewport = shadow.getViewport( vp ); - canvas.width = width; - canvas.height = height; + _viewport.set( + _viewportSize.x * viewport.x, + _viewportSize.y * viewport.y, + _viewportSize.x * viewport.z, + _viewportSize.y * viewport.w + ); - const context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); + _state.viewport( _viewport ); - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); + shadow.updateMatrices( light, vp ); - return canvas; + _frustum = shadow.getFrustum(); - } else { + renderObject( scene, camera, shadow.camera, light, this.type ); - if ( 'data' in image ) { + } - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); + // do blur pass for VSM - } + if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { - return image; + VSMPass( shadow, camera ); } + shadow.needsUpdate = false; + } - return image; - - } + scope.needsUpdate = false; - function isPowerOfTwo$1( image ) { + _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); + }; - } + function VSMPass( shadow, camera ) { - function textureNeedsPowerOfTwo( texture ) { + const geometry = _objects.update( fullScreenMesh ); - if ( isWebGL2 ) return false; + if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); + shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; + shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; - } + shadowMaterialVertical.needsUpdate = true; + shadowMaterialHorizontal.needsUpdate = true; - function textureNeedsGenerateMipmaps( texture, supportsMips ) { + } - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + // vertical pass - } + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.mapPass ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - function generateMipmap( target, texture, width, height ) { + // horizontal pass - _gl.generateMipmap( target ); + shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); - const textureProperties = properties.get( texture ); + } - textureProperties.__maxMipLevel = Math.log2( Math.max( width, height ) ); + function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar, type ) { - } + let result = null; - function getInternalFormat( internalFormatName, glFormat, glType ) { + const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; - if ( isWebGL2 === false ) return glFormat; + if ( customMaterial !== undefined ) { - if ( internalFormatName !== null ) { + result = customMaterial; - if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; + } else { - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); + result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; } - let internalFormat = glFormat; + if ( ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) || + ( material.displacementMap && material.displacementScale !== 0 ) || + ( material.alphaMap && material.alphaTest > 0 ) ) { - if ( glFormat === 6403 ) { + // in this case we need a unique material instance reflecting the + // appropriate state - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; + const keyA = result.uuid, keyB = material.uuid; - } + let materialsForVariant = _materialCache[ keyA ]; - if ( glFormat === 6407 ) { + if ( materialsForVariant === undefined ) { - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; + materialsForVariant = {}; + _materialCache[ keyA ] = materialsForVariant; - } + } - if ( glFormat === 6408 ) { + let cachedMaterial = materialsForVariant[ keyB ]; - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; + if ( cachedMaterial === undefined ) { - } + cachedMaterial = result.clone(); + materialsForVariant[ keyB ] = cachedMaterial; - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { + } - extensions.get( 'EXT_color_buffer_float' ); + result = cachedMaterial; } - return internalFormat; - - } + result.visible = material.visible; + result.wireframe = material.wireframe; - // Fallback filters for non-power-of-2 textures + if ( type === VSMShadowMap ) { - function filterFallback( f ) { + result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { + } else { - return 9728; + result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; } - return 9729; - - } - - // - - function onTextureDispose( event ) { + result.alphaMap = material.alphaMap; + result.alphaTest = material.alphaTest; - const texture = event.target; + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; + result.clipIntersection = material.clipIntersection; - texture.removeEventListener( 'dispose', onTextureDispose ); + result.displacementMap = material.displacementMap; + result.displacementScale = material.displacementScale; + result.displacementBias = material.displacementBias; - deallocateTexture( texture ); + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; - if ( texture.isVideoTexture ) { + if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - _videoTextures.delete( texture ); + result.referencePosition.setFromMatrixPosition( light.matrixWorld ); + result.nearDistance = shadowCameraNear; + result.farDistance = shadowCameraFar; } - info.memory.textures --; + return result; } - function onRenderTargetDispose( event ) { - - const renderTarget = event.target; + function renderObject( object, camera, shadowCamera, light, type ) { - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + if ( object.visible === false ) return; - deallocateRenderTarget( renderTarget ); + const visible = object.layers.test( camera.layers ); - info.memory.textures --; + if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - } + if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - // + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - function deallocateTexture( texture ) { + const geometry = _objects.update( object ); + const material = object.material; - const textureProperties = properties.get( texture ); + if ( Array.isArray( material ) ) { - if ( textureProperties.__webglInit === undefined ) return; + const groups = geometry.groups; - _gl.deleteTexture( textureProperties.__webglTexture ); + for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - properties.remove( texture ); + const group = groups[ k ]; + const groupMaterial = material[ group.materialIndex ]; - } + if ( groupMaterial && groupMaterial.visible ) { - function deallocateRenderTarget( renderTarget ) { + const depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); - const texture = renderTarget.texture; + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); + } - if ( ! renderTarget ) return; + } - if ( textureProperties.__webglTexture !== undefined ) { + } else if ( material.visible ) { - _gl.deleteTexture( textureProperties.__webglTexture ); + const depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far, type ); - } + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - if ( renderTarget.depthTexture ) { + } - renderTarget.depthTexture.dispose(); + } } - if ( renderTarget.isWebGLCubeRenderTarget ) { - - for ( let i = 0; i < 6; i ++ ) { - - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - - } + const children = object.children; - } else { + for ( let i = 0, l = children.length; i < l; i ++ ) { - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); - if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); + renderObject( children[ i ], camera, shadowCamera, light, type ); } - properties.remove( texture ); - properties.remove( renderTarget ); - } - // +} - let textureUnits = 0; +function WebGLState( gl, extensions, capabilities ) { - function resetTextureUnits() { + const isWebGL2 = capabilities.isWebGL2; - textureUnits = 0; + function ColorBuffer() { - } + let locked = false; - function allocateTextureUnit() { + const color = new Vector4(); + let currentColorMask = null; + const currentColorClear = new Vector4( 0, 0, 0, 0 ); - const textureUnit = textureUnits; + return { - if ( textureUnit >= maxTextures ) { + setMask: function ( colorMask ) { - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); + if ( currentColorMask !== colorMask && ! locked ) { - } + gl.colorMask( colorMask, colorMask, colorMask, colorMask ); + currentColorMask = colorMask; - textureUnits += 1; + } - return textureUnit; + }, - } + setLocked: function ( lock ) { - // + locked = lock; - function setTexture2D( texture, slot ) { + }, - const textureProperties = properties.get( texture ); + setClear: function ( r, g, b, a, premultipliedAlpha ) { - if ( texture.isVideoTexture ) updateVideoTexture( texture ); + if ( premultipliedAlpha === true ) { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + r *= a; g *= a; b *= a; - const image = texture.image; + } - if ( image === undefined ) { + color.set( r, g, b, a ); - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); + if ( currentColorClear.equals( color ) === false ) { - } else if ( image.complete === false ) { + gl.clearColor( r, g, b, a ); + currentColorClear.copy( color ); - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); + } - } else { + }, - uploadTexture( textureProperties, texture, slot ); - return; + reset: function () { - } + locked = false; - } + currentColorMask = null; + currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); + } - } + }; - function setTexture2DArray( texture, slot ) { + } - const textureProperties = properties.get( texture ); + function DepthBuffer() { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + let locked = false; - uploadTexture( textureProperties, texture, slot ); - return; + let currentDepthMask = null; + let currentDepthFunc = null; + let currentDepthClear = null; - } + return { - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); + setTest: function ( depthTest ) { - } + if ( depthTest ) { - function setTexture3D( texture, slot ) { + enable( 2929 ); - const textureProperties = properties.get( texture ); + } else { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + disable( 2929 ); - uploadTexture( textureProperties, texture, slot ); - return; + } - } + }, - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); + setMask: function ( depthMask ) { - } + if ( currentDepthMask !== depthMask && ! locked ) { - function setTextureCube( texture, slot ) { + gl.depthMask( depthMask ); + currentDepthMask = depthMask; - const textureProperties = properties.get( texture ); + } - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + }, - uploadCubeTexture( textureProperties, texture, slot ); - return; + setFunc: function ( depthFunc ) { - } + if ( currentDepthFunc !== depthFunc ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + if ( depthFunc ) { - } + switch ( depthFunc ) { - const wrappingToGL = { - [ RepeatWrapping ]: 10497, - [ ClampToEdgeWrapping ]: 33071, - [ MirroredRepeatWrapping ]: 33648 - }; + case NeverDepth: - const filterToGL = { - [ NearestFilter ]: 9728, - [ NearestMipmapNearestFilter ]: 9984, - [ NearestMipmapLinearFilter ]: 9986, + gl.depthFunc( 512 ); + break; - [ LinearFilter ]: 9729, - [ LinearMipmapNearestFilter ]: 9985, - [ LinearMipmapLinearFilter ]: 9987 - }; + case AlwaysDepth: - function setTextureParameters( textureType, texture, supportsMips ) { + gl.depthFunc( 519 ); + break; - if ( supportsMips ) { + case LessDepth: - _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); - _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); + gl.depthFunc( 513 ); + break; - if ( textureType === 32879 || textureType === 35866 ) { + case LessEqualDepth: - _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); + gl.depthFunc( 515 ); + break; - } + case EqualDepth: - _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); - _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); + gl.depthFunc( 514 ); + break; - } else { + case GreaterEqualDepth: - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); + gl.depthFunc( 518 ); + break; - if ( textureType === 32879 || textureType === 35866 ) { + case GreaterDepth: - _gl.texParameteri( textureType, 32882, 33071 ); + gl.depthFunc( 516 ); + break; - } + case NotEqualDepth: - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { + gl.depthFunc( 517 ); + break; - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); + default: - } + gl.depthFunc( 515 ); - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); + } - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { + } else { - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); + gl.depthFunc( 515 ); - } + } - } + currentDepthFunc = depthFunc; - if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { + } - const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + }, - if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 - if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only + setLocked: function ( lock ) { - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + locked = lock; - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; + }, - } + setClear: function ( depth ) { - } + if ( currentDepthClear !== depth ) { - } + gl.clearDepth( depth ); + currentDepthClear = depth; - function initTexture( textureProperties, texture ) { + } - if ( textureProperties.__webglInit === undefined ) { + }, - textureProperties.__webglInit = true; + reset: function () { - texture.addEventListener( 'dispose', onTextureDispose ); + locked = false; - textureProperties.__webglTexture = _gl.createTexture(); + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; - info.memory.textures ++; + } - } + }; } - function uploadTexture( textureProperties, texture, slot ) { - - let textureType = 3553; + function StencilBuffer() { - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; + let locked = false; - initTexture( textureProperties, texture ); + let currentStencilMask = null; + let currentStencilFunc = null; + let currentStencilRef = null; + let currentStencilFuncMask = null; + let currentStencilFail = null; + let currentStencilZFail = null; + let currentStencilZPass = null; + let currentStencilClear = null; - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); + return { - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); + setTest: function ( stencilTest ) { - const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + if ( ! locked ) { - const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ); + if ( stencilTest ) { - let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + enable( 2960 ); - setTextureParameters( textureType, texture, supportsMips ); + } else { - let mipmap; - const mipmaps = texture.mipmaps; + disable( 2960 ); - if ( texture.isDepthTexture ) { + } - // populate depth texture with dummy data + } - glInternalFormat = 6402; + }, - if ( isWebGL2 ) { + setMask: function ( stencilMask ) { - if ( texture.type === FloatType ) { + if ( currentStencilMask !== stencilMask && ! locked ) { - glInternalFormat = 36012; + gl.stencilMask( stencilMask ); + currentStencilMask = stencilMask; - } else if ( texture.type === UnsignedIntType ) { + } - glInternalFormat = 33190; + }, - } else if ( texture.type === UnsignedInt248Type ) { + setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - glInternalFormat = 35056; + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilFuncMask !== stencilMask ) { - } else { + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; } - } else { + }, - if ( texture.type === FloatType ) { + setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { - } + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - } + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; - // validation checks for WebGL 1 + } - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { + }, - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { + setLocked: function ( lock ) { - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); + locked = lock; - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); + }, - } + setClear: function ( stencil ) { - } + if ( currentStencilClear !== stencil ) { - if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { + gl.clearStencil( stencil ); + currentStencilClear = stencil; - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - glInternalFormat = 34041; + } - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { + }, - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); + reset: function () { - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); + locked = false; - } + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; } - // + }; - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); + } - } else if ( texture.isDataTexture ) { + // - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + const colorBuffer = new ColorBuffer(); + const depthBuffer = new DepthBuffer(); + const stencilBuffer = new StencilBuffer(); - if ( mipmaps.length > 0 && supportsMips ) { + let enabledCapabilities = {}; - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + let currentBoundFramebuffers = {}; + let currentDrawbuffers = new WeakMap(); + let defaultDrawbuffers = []; - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + let currentProgram = null; - } + let currentBlendingEnabled = false; + let currentBlending = null; + let currentBlendEquation = null; + let currentBlendSrc = null; + let currentBlendDst = null; + let currentBlendEquationAlpha = null; + let currentBlendSrcAlpha = null; + let currentBlendDstAlpha = null; + let currentPremultipledAlpha = false; - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + let currentFlipSided = null; + let currentCullFace = null; - } else { + let currentLineWidth = null; - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + let currentPolygonOffsetFactor = null; + let currentPolygonOffsetUnits = null; - } + const maxTextures = gl.getParameter( 35661 ); - } else if ( texture.isCompressedTexture ) { + let lineWidthAvailable = false; + let version = 0; + const glVersion = gl.getParameter( 7938 ); - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - mipmap = mipmaps[ i ]; + version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 1.0 ); - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - if ( glFormat !== null ) { + version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 2.0 ); - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } - } else { + let currentTextureSlot = null; + let currentBoundTextures = {}; - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); + const scissorParam = gl.getParameter( 3088 ); + const viewportParam = gl.getParameter( 2978 ); - } + const currentScissor = new Vector4().fromArray( scissorParam ); + const currentViewport = new Vector4().fromArray( viewportParam ); - } else { + function createTexture( type, target, count ) { - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - - } - - } + const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. + const texture = gl.createTexture(); - textureProperties.__maxMipLevel = mipmaps.length - 1; + gl.bindTexture( type, texture ); + gl.texParameteri( type, 10241, 9728 ); + gl.texParameteri( type, 10240, 9728 ); - } else if ( texture.isDataTexture2DArray ) { + for ( let i = 0; i < count; i ++ ) { - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); - } else if ( texture.isDataTexture3D ) { + } - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + return texture; - } else { + } - // regular Texture (image, video, canvas) + const emptyTextures = {}; + emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); + emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + // init - if ( mipmaps.length > 0 && supportsMips ) { + colorBuffer.setClear( 0, 0, 0, 1 ); + depthBuffer.setClear( 1 ); + stencilBuffer.setClear( 0 ); - for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { + enable( 2929 ); + depthBuffer.setFunc( LessEqualDepth ); - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); + setFlipSided( false ); + setCullFace( CullFaceBack ); + enable( 2884 ); - } + setBlending( NoBlending ); - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + // - } else { + function enable( id ) { - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; + if ( enabledCapabilities[ id ] !== true ) { - } + gl.enable( id ); + enabledCapabilities[ id ] = true; } - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + } - generateMipmap( textureType, texture, image.width, image.height ); + function disable( id ) { - } + if ( enabledCapabilities[ id ] !== false ) { - textureProperties.__version = texture.version; + gl.disable( id ); + enabledCapabilities[ id ] = false; - if ( texture.onUpdate ) texture.onUpdate( texture ); + } } - function uploadCubeTexture( textureProperties, texture, slot ) { + function bindFramebuffer( target, framebuffer ) { - if ( texture.image.length !== 6 ) return; + if ( currentBoundFramebuffers[ target ] !== framebuffer ) { - initTexture( textureProperties, texture ); + gl.bindFramebuffer( target, framebuffer ); - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + currentBoundFramebuffers[ target ] = framebuffer; - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); - _gl.pixelStorei( 37443, 0 ); + if ( isWebGL2 ) { - const isCompressed = ( texture && ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ) ); - const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); + // 36009 is equivalent to 36160 - const cubeImage = []; + if ( target === 36009 ) { - for ( let i = 0; i < 6; i ++ ) { + currentBoundFramebuffers[ 36160 ] = framebuffer; - if ( ! isCompressed && ! isDataTexture ) { + } - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); + if ( target === 36160 ) { - } else { + currentBoundFramebuffers[ 36009 ] = framebuffer; - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + } } - } + return true; - const image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo$1( image ) || isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + } - setTextureParameters( 34067, texture, supportsMips ); + return false; - let mipmaps; + } - if ( isCompressed ) { + function drawBuffers( renderTarget, framebuffer ) { - for ( let i = 0; i < 6; i ++ ) { + let drawBuffers = defaultDrawbuffers; - mipmaps = cubeImage[ i ].mipmaps; + let needsUpdate = false; - for ( let j = 0; j < mipmaps.length; j ++ ) { + if ( renderTarget ) { - const mipmap = mipmaps[ j ]; + drawBuffers = currentDrawbuffers.get( framebuffer ); - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + if ( drawBuffers === undefined ) { - if ( glFormat !== null ) { + drawBuffers = []; + currentDrawbuffers.set( framebuffer, drawBuffers ); - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } - } else { + if ( renderTarget.isWebGLMultipleRenderTargets ) { - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); + const textures = renderTarget.texture; - } + if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== 36064 ) { - } else { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + drawBuffers[ i ] = 36064 + i; } - } - - } - - textureProperties.__maxMipLevel = mipmaps.length - 1; + drawBuffers.length = textures.length; - } else { - - mipmaps = texture.mipmaps; - - for ( let i = 0; i < 6; i ++ ) { + needsUpdate = true; - if ( isDataTexture ) { - - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - - for ( let j = 0; j < mipmaps.length; j ++ ) { + } - const mipmap = mipmaps[ j ]; - const mipmapImage = mipmap.image[ i ].image; + } else { - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); + if ( drawBuffers[ 0 ] !== 36064 ) { - } + drawBuffers[ 0 ] = 36064; - } else { + needsUpdate = true; - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); + } - for ( let j = 0; j < mipmaps.length; j ++ ) { + } - const mipmap = mipmaps[ j ]; + } else { - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); + if ( drawBuffers[ 0 ] !== 1029 ) { - } + drawBuffers[ 0 ] = 1029; - } + needsUpdate = true; } - textureProperties.__maxMipLevel = mipmaps.length; - } - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + if ( needsUpdate ) { - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); + if ( capabilities.isWebGL2 ) { - } + gl.drawBuffers( drawBuffers ); - textureProperties.__version = texture.version; + } else { - if ( texture.onUpdate ) texture.onUpdate( texture ); + extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers ); - } + } - // Render targets + } - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { - const texture = renderTarget.texture; + } - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + function useProgram( program ) { - if ( textureTarget === 32879 || textureTarget === 35866 ) { + if ( currentProgram !== program ) { - state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); + gl.useProgram( program ); - } else { + currentProgram = program; - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + return true; } - state.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); - state.bindFramebuffer( 36160, null ); + return false; } - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - - _gl.bindRenderbuffer( 36161, renderbuffer ); + const equationToGL = { + [ AddEquation ]: 32774, + [ SubtractEquation ]: 32778, + [ ReverseSubtractEquation ]: 32779 + }; - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + if ( isWebGL2 ) { - let glInternalFormat = 33189; + equationToGL[ MinEquation ] = 32775; + equationToGL[ MaxEquation ] = 32776; - if ( isMultisample ) { + } else { - const depthTexture = renderTarget.depthTexture; + const extension = extensions.get( 'EXT_blend_minmax' ); - if ( depthTexture && depthTexture.isDepthTexture ) { + if ( extension !== null ) { - if ( depthTexture.type === FloatType ) { + equationToGL[ MinEquation ] = extension.MIN_EXT; + equationToGL[ MaxEquation ] = extension.MAX_EXT; - glInternalFormat = 36012; + } - } else if ( depthTexture.type === UnsignedIntType ) { + } - glInternalFormat = 33190; + const factorToGL = { + [ ZeroFactor ]: 0, + [ OneFactor ]: 1, + [ SrcColorFactor ]: 768, + [ SrcAlphaFactor ]: 770, + [ SrcAlphaSaturateFactor ]: 776, + [ DstColorFactor ]: 774, + [ DstAlphaFactor ]: 772, + [ OneMinusSrcColorFactor ]: 769, + [ OneMinusSrcAlphaFactor ]: 771, + [ OneMinusDstColorFactor ]: 775, + [ OneMinusDstAlphaFactor ]: 773 + }; - } + function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - } + if ( blending === NoBlending ) { - const samples = getRenderTargetSamples( renderTarget ); + if ( currentBlendingEnabled === true ) { - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + disable( 3042 ); + currentBlendingEnabled = false; - } else { + } - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + return; - } + } - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); + if ( currentBlendingEnabled === false ) { - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + enable( 3042 ); + currentBlendingEnabled = true; - if ( isMultisample ) { + } - const samples = getRenderTargetSamples( renderTarget ); + if ( blending !== CustomBlending ) { - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - } else { + if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); + gl.blendEquation( 32774 ); - } + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; + } - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); + if ( premultipliedAlpha ) { - } else { + switch ( blending ) { - const texture = renderTarget.texture; + case NormalBlending: + gl.blendFuncSeparate( 1, 771, 1, 771 ); + break; - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); + case AdditiveBlending: + gl.blendFunc( 1, 1 ); + break; - if ( isMultisample ) { + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 769, 0, 1 ); + break; - const samples = getRenderTargetSamples( renderTarget ); + case MultiplyBlending: + gl.blendFuncSeparate( 0, 768, 0, 770 ); + break; - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - } else { + } - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + } else { - } + switch ( blending ) { - } + case NormalBlending: + gl.blendFuncSeparate( 770, 771, 1, 771 ); + break; - _gl.bindRenderbuffer( 36161, null ); + case AdditiveBlending: + gl.blendFunc( 770, 1 ); + break; - } + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 769, 0, 1 ); + break; - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { + case MultiplyBlending: + gl.blendFunc( 0, 768 ); + break; - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - state.bindFramebuffer( 36160, framebuffer ); + } - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { + } - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; - } + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { + } - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; + return; } - setTexture2D( renderTarget.depthTexture, 0 ); + // custom blending - const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - if ( renderTarget.depthTexture.format === DepthFormat ) { + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); + gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); + } - } else { + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - throw new Error( 'Unknown depthTexture format' ); + gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); + + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; } - } + currentBlending = blending; + currentPremultipledAlpha = null; - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { + } - const renderTargetProperties = properties.get( renderTarget ); + function setMaterial( material, frontFaceCW ) { - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + material.side === DoubleSide + ? disable( 2884 ) + : enable( 2884 ); - if ( renderTarget.depthTexture ) { + let flipSided = ( material.side === BackSide ); + if ( frontFaceCW ) flipSided = ! flipSided; - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); + setFlipSided( flipSided ); - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + ( material.blending === NormalBlending && material.transparent === false ) + ? setBlending( NoBlending ) + : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - } else { + depthBuffer.setFunc( material.depthFunc ); + depthBuffer.setTest( material.depthTest ); + depthBuffer.setMask( material.depthWrite ); + colorBuffer.setMask( material.colorWrite ); - if ( isCube ) { + const stencilWrite = material.stencilWrite; + stencilBuffer.setTest( stencilWrite ); + if ( stencilWrite ) { - renderTargetProperties.__webglDepthbuffer = []; + stencilBuffer.setMask( material.stencilWriteMask ); + stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); + stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - for ( let i = 0; i < 6; i ++ ) { + } - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - } + material.alphaToCoverage === true + ? enable( 32926 ) + : disable( 32926 ); - } else { + } - state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); + // - } + function setFlipSided( flipSided ) { - } + if ( currentFlipSided !== flipSided ) { - state.bindFramebuffer( 36160, null ); + if ( flipSided ) { - } + gl.frontFace( 2304 ); - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { + } else { - const texture = renderTarget.texture; + gl.frontFace( 2305 ); - const renderTargetProperties = properties.get( renderTarget ); - const textureProperties = properties.get( texture ); + } - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + currentFlipSided = flipSided; - textureProperties.__webglTexture = _gl.createTexture(); - textureProperties.__version = texture.version; + } - info.memory.textures ++; + } - const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray; - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; + function setCullFace( cullFace ) { - // Handles WebGL2 RGBFormat fallback - #18858 + if ( cullFace !== CullFaceNone ) { - if ( isWebGL2 && texture.format === RGBFormat && ( texture.type === FloatType || texture.type === HalfFloatType ) ) { + enable( 2884 ); - texture.format = RGBAFormat; + if ( cullFace !== currentCullFace ) { - console.warn( 'THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.' ); + if ( cullFace === CullFaceBack ) { - } + gl.cullFace( 1029 ); - // Setup framebuffer + } else if ( cullFace === CullFaceFront ) { - if ( isCube ) { + gl.cullFace( 1028 ); - renderTargetProperties.__webglFramebuffer = []; + } else { - for ( let i = 0; i < 6; i ++ ) { + gl.cullFace( 1032 ); - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + } } } else { - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + disable( 2884 ); - if ( isMultisample ) { + } - if ( isWebGL2 ) { + currentCullFace = cullFace; - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + } - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); + function setLineWidth( width ) { - const glFormat = utils.convert( texture.format ); - const glType = utils.convert( texture.type ); - const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType ); - const samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + if ( width !== currentLineWidth ) { - state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); + if ( lineWidthAvailable ) gl.lineWidth( width ); - if ( renderTarget.depthBuffer ) { + currentLineWidth = width; - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); + } - } + } - state.bindFramebuffer( 36160, null ); + function setPolygonOffset( polygonOffset, factor, units ) { + if ( polygonOffset ) { - } else { + enable( 32823 ); - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - } + gl.polygonOffset( factor, units ); - } + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; - } + } - // Setup color buffer + } else { - if ( isCube ) { + disable( 32823 ); - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, texture, supportsMips ); + } - for ( let i = 0; i < 6; i ++ ) { + } - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); + function setScissorTest( scissorTest ) { - } + if ( scissorTest ) { - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + enable( 3089 ); - generateMipmap( 34067, texture, renderTarget.width, renderTarget.height ); + } else { - } + disable( 3089 ); - state.bindTexture( 34067, null ); + } - } else { + } - let glTextureType = 3553; + // texture - if ( isRenderTarget3D ) { + function activeTexture( webglSlot ) { - // Render targets containing layers, i.e: Texture 3D and 2d arrays + if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; - if ( isWebGL2 ) { + if ( currentTextureSlot !== webglSlot ) { - const isTexture3D = texture.isDataTexture3D; - glTextureType = isTexture3D ? 32879 : 35866; + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; - } else { + } - console.warn( 'THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.' ); + } - } + function bindTexture( webglType, webglTexture ) { - } + if ( currentTextureSlot === null ) { - state.bindTexture( glTextureType, textureProperties.__webglTexture ); - setTextureParameters( glTextureType, texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, glTextureType ); + activeTexture(); - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + } - generateMipmap( 3553, texture, renderTarget.width, renderTarget.height ); + let boundTexture = currentBoundTextures[ currentTextureSlot ]; - } + if ( boundTexture === undefined ) { - state.bindTexture( 3553, null ); + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; } - // Setup depth and stencil buffers + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - if ( renderTarget.depthBuffer ) { + gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - setupDepthRenderbuffer( renderTarget ); + boundTexture.type = webglType; + boundTexture.texture = webglTexture; } } - function updateRenderTargetMipmap( renderTarget ) { - - const texture = renderTarget.texture; + function unbindTexture() { - const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; + const boundTexture = currentBoundTextures[ currentTextureSlot ]; - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; - const webglTexture = properties.get( texture ).__webglTexture; + gl.bindTexture( boundTexture.type, null ); - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); + boundTexture.type = undefined; + boundTexture.texture = undefined; } } - function updateMultisampleRenderTarget( renderTarget ) { + function compressedTexImage2D() { - if ( renderTarget.isWebGLMultisampleRenderTarget ) { + try { - if ( isWebGL2 ) { + gl.compressedTexImage2D.apply( gl, arguments ); - const width = renderTarget.width; - const height = renderTarget.height; - let mask = 16384; + } catch ( error ) { - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; + console.error( 'THREE.WebGLState:', error ); - const renderTargetProperties = properties.get( renderTarget ); + } - state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); + } - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); + function texSubImage2D() { - state.bindFramebuffer( 36008, null ); - state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); + try { - } else { + gl.texSubImage2D.apply( gl, arguments ); - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); } } - function getRenderTargetSamples( renderTarget ) { + function texSubImage3D() { - return ( isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( maxSamples, renderTarget.samples ) : 0; + try { - } + gl.texSubImage3D.apply( gl, arguments ); - function updateVideoTexture( texture ) { + } catch ( error ) { - const frame = info.render.frame; + console.error( 'THREE.WebGLState:', error ); - // Check the last frame we updated the VideoTexture + } - if ( _videoTextures.get( texture ) !== frame ) { + } - _videoTextures.set( texture, frame ); - texture.update(); + function compressedTexSubImage2D() { - } + try { - } + gl.compressedTexSubImage2D.apply( gl, arguments ); - // backwards compatibility + } catch ( error ) { - let warnedTexture2D = false; - let warnedTextureCube = false; + console.error( 'THREE.WebGLState:', error ); - function safeSetTexture2D( texture, slot ) { + } - if ( texture && texture.isWebGLRenderTarget ) { + } - if ( warnedTexture2D === false ) { + function texStorage2D() { - console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' ); - warnedTexture2D = true; + try { - } + gl.texStorage2D.apply( gl, arguments ); - texture = texture.texture; + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); - setTexture2D( texture, slot ); + } } - function safeSetTextureCube( texture, slot ) { + function texStorage3D() { - if ( texture && texture.isWebGLCubeRenderTarget ) { - - if ( warnedTextureCube === false ) { + try { - console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' ); - warnedTextureCube = true; + gl.texStorage3D.apply( gl, arguments ); - } + } catch ( error ) { - texture = texture.texture; + console.error( 'THREE.WebGLState:', error ); } + } - setTextureCube( texture, slot ); + function texImage2D() { - } + try { - // + gl.texImage2D.apply( gl, arguments ); - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; + } catch ( error ) { - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + console.error( 'THREE.WebGLState:', error ); - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; + } -} + } -function WebGLUtils( gl, extensions, capabilities ) { + function texImage3D() { - const isWebGL2 = capabilities.isWebGL2; + try { - function convert( p ) { + gl.texImage3D.apply( gl, arguments ); - let extension; + } catch ( error ) { - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; + console.error( 'THREE.WebGLState:', error ); - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; + } - if ( p === HalfFloatType ) { + } - if ( isWebGL2 ) return 5131; + // - extension = extensions.get( 'OES_texture_half_float' ); + function scissor( scissor ) { - if ( extension !== null ) { + if ( currentScissor.equals( scissor ) === false ) { - return extension.HALF_FLOAT_OES; + gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); + currentScissor.copy( scissor ); - } else { + } - return null; + } - } + function viewport( viewport ) { + + if ( currentViewport.equals( viewport ) === false ) { + + gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); + currentViewport.copy( viewport ); } - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; + } - // WebGL2 formats. + // - if ( p === RedIntegerFormat ) return 36244; - if ( p === RGFormat ) return 33319; - if ( p === RGIntegerFormat ) return 33320; - if ( p === RGBIntegerFormat ) return 36248; - if ( p === RGBAIntegerFormat ) return 36249; + function reset() { - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { + // reset state - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + gl.disable( 3042 ); + gl.disable( 2884 ); + gl.disable( 2929 ); + gl.disable( 32823 ); + gl.disable( 3089 ); + gl.disable( 2960 ); + gl.disable( 32926 ); - if ( extension !== null ) { + gl.blendEquation( 32774 ); + gl.blendFunc( 1, 0 ); + gl.blendFuncSeparate( 1, 0, 1, 0 ); - if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); - } else { + gl.depthMask( true ); + gl.depthFunc( 513 ); + gl.clearDepth( 1 ); - return null; + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( 519, 0, 0xffffffff ); + gl.stencilOp( 7680, 7680, 7680 ); + gl.clearStencil( 0 ); - } + gl.cullFace( 1029 ); + gl.frontFace( 2305 ); - } + gl.polygonOffset( 0, 0 ); - if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || - p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { + gl.activeTexture( 33984 ); - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + gl.bindFramebuffer( 36160, null ); - if ( extension !== null ) { + if ( isWebGL2 === true ) { - if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; - if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; - if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; - if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + gl.bindFramebuffer( 36009, null ); + gl.bindFramebuffer( 36008, null ); - } else { + } - return null; + gl.useProgram( null ); - } + gl.lineWidth( 1 ); - } + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); - if ( p === RGB_ETC1_Format ) { + // reset internals - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); + enabledCapabilities = {}; - if ( extension !== null ) { + currentTextureSlot = null; + currentBoundTextures = {}; - return extension.COMPRESSED_RGB_ETC1_WEBGL; + currentBoundFramebuffers = {}; + currentDrawbuffers = new WeakMap(); + defaultDrawbuffers = []; - } else { + currentProgram = null; - return null; + currentBlendingEnabled = false; + currentBlending = null; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentPremultipledAlpha = false; - } + currentFlipSided = null; + currentCullFace = null; - } + currentLineWidth = null; - if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { + currentPolygonOffsetFactor = null; + currentPolygonOffsetUnits = null; - extension = extensions.get( 'WEBGL_compressed_texture_etc' ); + currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); - if ( extension !== null ) { + colorBuffer.reset(); + depthBuffer.reset(); + stencilBuffer.reset(); - if ( p === RGB_ETC2_Format ) return extension.COMPRESSED_RGB8_ETC2; - if ( p === RGBA_ETC2_EAC_Format ) return extension.COMPRESSED_RGBA8_ETC2_EAC; + } - } + return { - } + buffers: { + color: colorBuffer, + depth: depthBuffer, + stencil: stencilBuffer + }, - if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || - p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || - p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || - p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || - p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format || - p === SRGB8_ALPHA8_ASTC_4x4_Format || p === SRGB8_ALPHA8_ASTC_5x4_Format || p === SRGB8_ALPHA8_ASTC_5x5_Format || - p === SRGB8_ALPHA8_ASTC_6x5_Format || p === SRGB8_ALPHA8_ASTC_6x6_Format || p === SRGB8_ALPHA8_ASTC_8x5_Format || - p === SRGB8_ALPHA8_ASTC_8x6_Format || p === SRGB8_ALPHA8_ASTC_8x8_Format || p === SRGB8_ALPHA8_ASTC_10x5_Format || - p === SRGB8_ALPHA8_ASTC_10x6_Format || p === SRGB8_ALPHA8_ASTC_10x8_Format || p === SRGB8_ALPHA8_ASTC_10x10_Format || - p === SRGB8_ALPHA8_ASTC_12x10_Format || p === SRGB8_ALPHA8_ASTC_12x12_Format ) { + enable: enable, + disable: disable, - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); + bindFramebuffer: bindFramebuffer, + drawBuffers: drawBuffers, - if ( extension !== null ) { + useProgram: useProgram, - // TODO Complete? + setBlending: setBlending, + setMaterial: setMaterial, - return p; + setFlipSided: setFlipSided, + setCullFace: setCullFace, - } else { + setLineWidth: setLineWidth, + setPolygonOffset: setPolygonOffset, - return null; + setScissorTest: setScissorTest, - } + activeTexture: activeTexture, + bindTexture: bindTexture, + unbindTexture: unbindTexture, + compressedTexImage2D: compressedTexImage2D, + texImage2D: texImage2D, + texImage3D: texImage3D, - } + texStorage2D: texStorage2D, + texStorage3D: texStorage3D, + texSubImage2D: texSubImage2D, + texSubImage3D: texSubImage3D, + compressedTexSubImage2D: compressedTexSubImage2D, - if ( p === RGBA_BPTC_Format ) { + scissor: scissor, + viewport: viewport, - extension = extensions.get( 'EXT_texture_compression_bptc' ); + reset: reset - if ( extension !== null ) { + }; - // TODO Complete? +} - return p; +function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - } else { + const isWebGL2 = capabilities.isWebGL2; + const maxTextures = capabilities.maxTextures; + const maxCubemapSize = capabilities.maxCubemapSize; + const maxTextureSize = capabilities.maxTextureSize; + const maxSamples = capabilities.maxSamples; + const multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null; + const supportsInvalidateFramebuffer = /OculusBrowser/g.test( navigator.userAgent ); - return null; + const _videoTextures = new WeakMap(); + let _canvas; - } + const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source - } + // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, + // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! + // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - if ( p === UnsignedInt248Type ) { + let useOffscreenCanvas = false; - if ( isWebGL2 ) return 34042; + try { - extension = extensions.get( 'WEBGL_depth_texture' ); + useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' + // eslint-disable-next-line compat/compat + && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; - if ( extension !== null ) { + } catch ( err ) { - return extension.UNSIGNED_INT_24_8_WEBGL; + // Ignore any errors - } else { + } - return null; + function createCanvas( width, height ) { - } + // Use OffscreenCanvas when available. Specially needed in web workers - } + return useOffscreenCanvas ? + // eslint-disable-next-line compat/compat + new OffscreenCanvas( width, height ) : createElementNS( 'canvas' ); } - return { convert: convert }; + function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { -} + let scale = 1; -class ArrayCamera extends PerspectiveCamera { + // handle case if texture exceeds max size - constructor( array = [] ) { + if ( image.width > maxSize || image.height > maxSize ) { - super(); + scale = maxSize / Math.max( image.width, image.height ); - this.cameras = array; + } - } + // only perform resize if necessary -} + if ( scale < 1 || needsPowerOfTwo === true ) { -ArrayCamera.prototype.isArrayCamera = true; + // only perform resize for certain image types -class Group extends Object3D { + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - constructor() { + const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; - super(); + const width = floor( scale * image.width ); + const height = floor( scale * image.height ); - this.type = 'Group'; + if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - } + // cube textures can't reuse the same canvas -} + const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; -Group.prototype.isGroup = true; + canvas.width = width; + canvas.height = height; -const _moveEvent = { type: 'move' }; + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); -class WebXRController { + console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - constructor() { + return canvas; - this._targetRay = null; - this._grip = null; - this._hand = null; + } else { - } + if ( 'data' in image ) { - getHandSpace() { + console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - if ( this._hand === null ) { + } - this._hand = new Group(); - this._hand.matrixAutoUpdate = false; - this._hand.visible = false; + return image; - this._hand.joints = {}; - this._hand.inputState = { pinching: false }; + } } - return this._hand; + return image; } - getTargetRaySpace() { + function isPowerOfTwo$1( image ) { - if ( this._targetRay === null ) { + return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - this._targetRay = new Group(); - this._targetRay.matrixAutoUpdate = false; - this._targetRay.visible = false; - this._targetRay.hasLinearVelocity = false; - this._targetRay.linearVelocity = new Vector3(); - this._targetRay.hasAngularVelocity = false; - this._targetRay.angularVelocity = new Vector3(); + } - } + function textureNeedsPowerOfTwo( texture ) { - return this._targetRay; + if ( isWebGL2 ) return false; + + return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || + ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } - getGripSpace() { + function textureNeedsGenerateMipmaps( texture, supportsMips ) { - if ( this._grip === null ) { + return texture.generateMipmaps && supportsMips && + texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - this._grip = new Group(); - this._grip.matrixAutoUpdate = false; - this._grip.visible = false; - this._grip.hasLinearVelocity = false; - this._grip.linearVelocity = new Vector3(); - this._grip.hasAngularVelocity = false; - this._grip.angularVelocity = new Vector3(); + } - } + function generateMipmap( target ) { - return this._grip; + _gl.generateMipmap( target ); } - dispatchEvent( event ) { - - if ( this._targetRay !== null ) { + function getInternalFormat( internalFormatName, glFormat, glType, encoding, isVideoTexture = false ) { - this._targetRay.dispatchEvent( event ); + if ( isWebGL2 === false ) return glFormat; - } + if ( internalFormatName !== null ) { - if ( this._grip !== null ) { + if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - this._grip.dispatchEvent( event ); + console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); } - if ( this._hand !== null ) { + let internalFormat = glFormat; - this._hand.dispatchEvent( event ); + if ( glFormat === 6403 ) { - } + if ( glType === 5126 ) internalFormat = 33326; + if ( glType === 5131 ) internalFormat = 33325; + if ( glType === 5121 ) internalFormat = 33321; - return this; + } - } + if ( glFormat === 33319 ) { - disconnect( inputSource ) { + if ( glType === 5126 ) internalFormat = 33328; + if ( glType === 5131 ) internalFormat = 33327; + if ( glType === 5121 ) internalFormat = 33323; - this.dispatchEvent( { type: 'disconnected', data: inputSource } ); + } - if ( this._targetRay !== null ) { + if ( glFormat === 6408 ) { - this._targetRay.visible = false; + if ( glType === 5126 ) internalFormat = 34836; + if ( glType === 5131 ) internalFormat = 34842; + if ( glType === 5121 ) internalFormat = ( encoding === sRGBEncoding && isVideoTexture === false ) ? 35907 : 32856; + if ( glType === 32819 ) internalFormat = 32854; + if ( glType === 32820 ) internalFormat = 32855; } - if ( this._grip !== null ) { + if ( internalFormat === 33325 || internalFormat === 33326 || + internalFormat === 33327 || internalFormat === 33328 || + internalFormat === 34842 || internalFormat === 34836 ) { - this._grip.visible = false; + extensions.get( 'EXT_color_buffer_float' ); } - if ( this._hand !== null ) { + return internalFormat; - this._hand.visible = false; + } - } + function getMipLevels( texture, image, supportsMips ) { - return this; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { - } + return Math.log2( Math.max( image.width, image.height ) ) + 1; - update( inputSource, frame, referenceSpace ) { + } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { - let inputPose = null; - let gripPose = null; - let handPose = null; + // user-defined mipmaps - const targetRay = this._targetRay; - const grip = this._grip; - const hand = this._hand; + return texture.mipmaps.length; - if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { + } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { - if ( targetRay !== null ) { + return image.mipmaps.length; - inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); + } else { - if ( inputPose !== null ) { + // texture without mipmaps (only base level) - targetRay.matrix.fromArray( inputPose.transform.matrix ); - targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); + return 1; - if ( inputPose.linearVelocity ) { + } - targetRay.hasLinearVelocity = true; - targetRay.linearVelocity.copy( inputPose.linearVelocity ); + } - } else { + // Fallback filters for non-power-of-2 textures - targetRay.hasLinearVelocity = false; + function filterFallback( f ) { - } + if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - if ( inputPose.angularVelocity ) { + return 9728; - targetRay.hasAngularVelocity = true; - targetRay.angularVelocity.copy( inputPose.angularVelocity ); + } - } else { + return 9729; - targetRay.hasAngularVelocity = false; + } - } + // - this.dispatchEvent( _moveEvent ); + function onTextureDispose( event ) { - } + const texture = event.target; - } + texture.removeEventListener( 'dispose', onTextureDispose ); - if ( hand && inputSource.hand ) { + deallocateTexture( texture ); - handPose = true; + if ( texture.isVideoTexture ) { - for ( const inputjoint of inputSource.hand.values() ) { + _videoTextures.delete( texture ); - // Update the joints groups with the XRJoint poses - const jointPose = frame.getJointPose( inputjoint, referenceSpace ); + } - if ( hand.joints[ inputjoint.jointName ] === undefined ) { + } - // The transform of this joint will be updated with the joint pose on each frame - const joint = new Group(); - joint.matrixAutoUpdate = false; - joint.visible = false; - hand.joints[ inputjoint.jointName ] = joint; - // ?? - hand.add( joint ); + function onRenderTargetDispose( event ) { - } + const renderTarget = event.target; - const joint = hand.joints[ inputjoint.jointName ]; + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - if ( jointPose !== null ) { + deallocateRenderTarget( renderTarget ); - joint.matrix.fromArray( jointPose.transform.matrix ); - joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); - joint.jointRadius = jointPose.radius; + } - } + // - joint.visible = jointPose !== null; + function deallocateTexture( texture ) { - } + const textureProperties = properties.get( texture ); - // Custom events + if ( textureProperties.__webglInit === undefined ) return; - // Check pinchz - const indexTip = hand.joints[ 'index-finger-tip' ]; - const thumbTip = hand.joints[ 'thumb-tip' ]; - const distance = indexTip.position.distanceTo( thumbTip.position ); + // check if it's necessary to remove the WebGLTexture object - const distanceToPinch = 0.02; - const threshold = 0.005; + const source = texture.source; + const webglTextures = _sources.get( source ); - if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { + if ( webglTextures ) { - hand.inputState.pinching = false; - this.dispatchEvent( { - type: 'pinchend', - handedness: inputSource.handedness, - target: this - } ); + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; + webglTexture.usedTimes --; - } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { + // the WebGLTexture object is not used anymore, remove it - hand.inputState.pinching = true; - this.dispatchEvent( { - type: 'pinchstart', - handedness: inputSource.handedness, - target: this - } ); + if ( webglTexture.usedTimes === 0 ) { - } + deleteTexture( texture ); - } else { + } - if ( grip !== null && inputSource.gripSpace ) { + // remove the weak map entry if no WebGLTexture uses the source anymore - gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); + if ( Object.keys( webglTextures ).length === 0 ) { - if ( gripPose !== null ) { + _sources.delete( source ); - grip.matrix.fromArray( gripPose.transform.matrix ); - grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); + } - if ( gripPose.linearVelocity ) { + } - grip.hasLinearVelocity = true; - grip.linearVelocity.copy( gripPose.linearVelocity ); + properties.remove( texture ); - } else { + } - grip.hasLinearVelocity = false; + function deleteTexture( texture ) { - } + const textureProperties = properties.get( texture ); + _gl.deleteTexture( textureProperties.__webglTexture ); - if ( gripPose.angularVelocity ) { + const source = texture.source; + const webglTextures = _sources.get( source ); + delete webglTextures[ textureProperties.__cacheKey ]; - grip.hasAngularVelocity = true; - grip.angularVelocity.copy( gripPose.angularVelocity ); + info.memory.textures --; - } else { + } - grip.hasAngularVelocity = false; + function deallocateRenderTarget( renderTarget ) { - } + const texture = renderTarget.texture; - } + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - } + if ( textureProperties.__webglTexture !== undefined ) { - } + _gl.deleteTexture( textureProperties.__webglTexture ); + + info.memory.textures --; } - if ( targetRay !== null ) { + if ( renderTarget.depthTexture ) { - targetRay.visible = ( inputPose !== null ); + renderTarget.depthTexture.dispose(); } - if ( grip !== null ) { + if ( renderTarget.isWebGLCubeRenderTarget ) { - grip.visible = ( gripPose !== null ); + for ( let i = 0; i < 6; i ++ ) { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); + + } + + } else { + + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); + if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); + if ( renderTargetProperties.__webglColorRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer ); + if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } - if ( hand !== null ) { + if ( renderTarget.isWebGLMultipleRenderTargets ) { - hand.visible = ( handPose !== null ); + for ( let i = 0, il = texture.length; i < il; i ++ ) { + + const attachmentProperties = properties.get( texture[ i ] ); + + if ( attachmentProperties.__webglTexture ) { + + _gl.deleteTexture( attachmentProperties.__webglTexture ); + + info.memory.textures --; + + } + + properties.remove( texture[ i ] ); + + } } - return this; + properties.remove( texture ); + properties.remove( renderTarget ); } -} + // -class WebXRManager extends EventDispatcher$1 { + let textureUnits = 0; - constructor( renderer, gl ) { + function resetTextureUnits() { - super(); + textureUnits = 0; - const scope = this; - const state = renderer.state; + } - let session = null; + function allocateTextureUnit() { - let framebufferScaleFactor = 1.0; + const textureUnit = textureUnits; - let referenceSpace = null; - let referenceSpaceType = 'local-floor'; + if ( textureUnit >= maxTextures ) { - let pose = null; + console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - const controllers = []; - const inputSourcesMap = new Map(); + } - // + textureUnits += 1; - const cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); + return textureUnit; - const cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); + } - const cameras = [ cameraL, cameraR ]; + function getTextureCacheKey( texture ) { - const cameraVR = new ArrayCamera(); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); + const array = []; - let _currentDepthNear = null; - let _currentDepthFar = null; + array.push( texture.wrapS ); + array.push( texture.wrapT ); + array.push( texture.magFilter ); + array.push( texture.minFilter ); + array.push( texture.anisotropy ); + array.push( texture.internalFormat ); + array.push( texture.format ); + array.push( texture.type ); + array.push( texture.generateMipmaps ); + array.push( texture.premultiplyAlpha ); + array.push( texture.flipY ); + array.push( texture.unpackAlignment ); + array.push( texture.encoding ); - // + return array.join(); - this.enabled = false; + } - this.isPresenting = false; + // - this.getController = function ( index ) { + function setTexture2D( texture, slot ) { - let controller = controllers[ index ]; + const textureProperties = properties.get( texture ); - if ( controller === undefined ) { + if ( texture.isVideoTexture ) updateVideoTexture( texture ); - controller = new WebXRController(); - controllers[ index ] = controller; + if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { - } + const image = texture.image; - return controller.getTargetRaySpace(); + if ( image === null ) { - }; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' ); - this.getControllerGrip = function ( index ) { + } else if ( image.complete === false ) { - let controller = controllers[ index ]; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - if ( controller === undefined ) { + } else { - controller = new WebXRController(); - controllers[ index ] = controller; + uploadTexture( textureProperties, texture, slot ); + return; } - return controller.getGripSpace(); - - }; + } - this.getHand = function ( index ) { + state.activeTexture( 33984 + slot ); + state.bindTexture( 3553, textureProperties.__webglTexture ); - let controller = controllers[ index ]; + } - if ( controller === undefined ) { + function setTexture2DArray( texture, slot ) { - controller = new WebXRController(); - controllers[ index ] = controller; + const textureProperties = properties.get( texture ); - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - return controller.getHandSpace(); + uploadTexture( textureProperties, texture, slot ); + return; - }; + } - // + state.activeTexture( 33984 + slot ); + state.bindTexture( 35866, textureProperties.__webglTexture ); - function onSessionEvent( event ) { + } - const controller = inputSourcesMap.get( event.inputSource ); + function setTexture3D( texture, slot ) { - if ( controller ) { + const textureProperties = properties.get( texture ); - controller.dispatchEvent( { type: event.type, data: event.inputSource } ); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - } + uploadTexture( textureProperties, texture, slot ); + return; } - function onSessionEnd() { + state.activeTexture( 33984 + slot ); + state.bindTexture( 32879, textureProperties.__webglTexture ); - inputSourcesMap.forEach( function ( controller, inputSource ) { + } - controller.disconnect( inputSource ); + function setTextureCube( texture, slot ) { - } ); + const textureProperties = properties.get( texture ); - inputSourcesMap.clear(); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - _currentDepthNear = null; - _currentDepthFar = null; + uploadCubeTexture( textureProperties, texture, slot ); + return; - // restore framebuffer/rendering state + } - state.bindXRFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - // + } - animation.stop(); + const wrappingToGL = { + [ RepeatWrapping ]: 10497, + [ ClampToEdgeWrapping ]: 33071, + [ MirroredRepeatWrapping ]: 33648 + }; - scope.isPresenting = false; + const filterToGL = { + [ NearestFilter ]: 9728, + [ NearestMipmapNearestFilter ]: 9984, + [ NearestMipmapLinearFilter ]: 9986, - scope.dispatchEvent( { type: 'sessionend' } ); + [ LinearFilter ]: 9729, + [ LinearMipmapNearestFilter ]: 9985, + [ LinearMipmapLinearFilter ]: 9987 + }; - } + function setTextureParameters( textureType, texture, supportsMips ) { - this.setFramebufferScaleFactor = function ( value ) { + if ( supportsMips ) { - framebufferScaleFactor = value; + _gl.texParameteri( textureType, 10242, wrappingToGL[ texture.wrapS ] ); + _gl.texParameteri( textureType, 10243, wrappingToGL[ texture.wrapT ] ); - if ( scope.isPresenting === true ) { + if ( textureType === 32879 || textureType === 35866 ) { - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); + _gl.texParameteri( textureType, 32882, wrappingToGL[ texture.wrapR ] ); } - }; + _gl.texParameteri( textureType, 10240, filterToGL[ texture.magFilter ] ); + _gl.texParameteri( textureType, 10241, filterToGL[ texture.minFilter ] ); - this.setReferenceSpaceType = function ( value ) { + } else { - referenceSpaceType = value; + _gl.texParameteri( textureType, 10242, 33071 ); + _gl.texParameteri( textureType, 10243, 33071 ); - if ( scope.isPresenting === true ) { + if ( textureType === 32879 || textureType === 35866 ) { - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); + _gl.texParameteri( textureType, 32882, 33071 ); } - }; + if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - this.getReferenceSpace = function () { + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - return referenceSpace; + } - }; + _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); - this.getSession = function () { + if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - return session; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - }; + } - this.setSession = async function ( value ) { + } - session = value; + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - if ( session !== null ) { + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'squeeze', onSessionEvent ); - session.addEventListener( 'squeezestart', onSessionEvent ); - session.addEventListener( 'squeezeend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); - session.addEventListener( 'inputsourceschange', onInputSourcesChange ); + if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 + if ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only - const attributes = gl.getContextAttributes(); + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - if ( attributes.xrCompatible !== true ) { + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; - await gl.makeXRCompatible(); + } - } + } - const layerInit = { - antialias: attributes.antialias, - alpha: attributes.alpha, - depth: attributes.depth, - stencil: attributes.stencil, - framebufferScaleFactor: framebufferScaleFactor - }; + } - // eslint-disable-next-line no-undef - const baseLayer = new XRWebGLLayer( session, gl, layerInit ); + function initTexture( textureProperties, texture ) { - session.updateRenderState( { baseLayer: baseLayer } ); + let forceUpload = false; - referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); + if ( textureProperties.__webglInit === undefined ) { - animation.setContext( session ); - animation.start(); + textureProperties.__webglInit = true; - scope.isPresenting = true; + texture.addEventListener( 'dispose', onTextureDispose ); - scope.dispatchEvent( { type: 'sessionstart' } ); + } - } + // create Source <-> WebGLTextures mapping if necessary - }; + const source = texture.source; + let webglTextures = _sources.get( source ); - function onInputSourcesChange( event ) { + if ( webglTextures === undefined ) { - const inputSources = session.inputSources; + webglTextures = {}; + _sources.set( source, webglTextures ); - // Assign inputSources to available controllers + } - for ( let i = 0; i < controllers.length; i ++ ) { + // check if there is already a WebGLTexture object for the given texture parameters - inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); + const textureCacheKey = getTextureCacheKey( texture ); - } + if ( textureCacheKey !== textureProperties.__cacheKey ) { - // Notify disconnected + // if not, create a new instance of WebGLTexture - for ( let i = 0; i < event.removed.length; i ++ ) { + if ( webglTextures[ textureCacheKey ] === undefined ) { - const inputSource = event.removed[ i ]; - const controller = inputSourcesMap.get( inputSource ); + // create new entry - if ( controller ) { + webglTextures[ textureCacheKey ] = { + texture: _gl.createTexture(), + usedTimes: 0 + }; - controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); - inputSourcesMap.delete( inputSource ); + info.memory.textures ++; - } + // when a new instance of WebGLTexture was created, a texture upload is required + // even if the image contents are identical + + forceUpload = true; } - // Notify connected + webglTextures[ textureCacheKey ].usedTimes ++; - for ( let i = 0; i < event.added.length; i ++ ) { + // every time the texture cache key changes, it's necessary to check if an instance of + // WebGLTexture can be deleted in order to avoid a memory leak. - const inputSource = event.added[ i ]; - const controller = inputSourcesMap.get( inputSource ); + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; - if ( controller ) { + if ( webglTexture !== undefined ) { - controller.dispatchEvent( { type: 'connected', data: inputSource } ); + webglTextures[ textureProperties.__cacheKey ].usedTimes --; + + if ( webglTexture.usedTimes === 0 ) { + + deleteTexture( texture ); } } - } + // store references to cache key and WebGLTexture object - // + textureProperties.__cacheKey = textureCacheKey; + textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; - const cameraLPos = new Vector3(); - const cameraRPos = new Vector3(); + } - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { + return forceUpload; - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); + } - const ipd = cameraLPos.distanceTo( cameraRPos ); + function uploadTexture( textureProperties, texture, slot ) { - const projL = cameraL.projectionMatrix.elements; - const projR = cameraR.projectionMatrix.elements; + let textureType = 3553; - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; + if ( texture.isDataArrayTexture ) textureType = 35866; + if ( texture.isData3DTexture ) textureType = 32879; - const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - const left = near * leftFov; - const right = near * rightFov; + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - const zOffset = ipd / ( - leftFov + rightFov ); - const xOffset = zOffset * - leftFov; + state.activeTexture( 33984 + slot ); + state.bindTexture( textureType, textureProperties.__webglTexture ); - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + if ( source.version !== source.__currentVersion || forceUpload === true ) { - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - const near2 = near + zOffset; - const far2 = far + zOffset; - const left2 = left - xOffset; - const right2 = right + ( ipd - xOffset ); - const top2 = topFov * far / far2 * near2; - const bottom2 = bottomFov * far / far2 * near2; + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; + let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + image = verifyColorSpace( texture, image ); - } + const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.encoding ); - function updateCamera( camera, parent ) { + let glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture ); - if ( parent === null ) { + setTextureParameters( textureType, texture, supportsMips ); - camera.matrixWorld.copy( camera.matrix ); + let mipmap; + const mipmaps = texture.mipmaps; - } else { + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( textureProperties.__version === undefined ); + const levels = getMipLevels( texture, image, supportsMips ); - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); + if ( texture.isDepthTexture ) { - } + // populate depth texture with dummy data - camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + glInternalFormat = 6402; - } + if ( isWebGL2 ) { - this.getCamera = function ( camera ) { + if ( texture.type === FloatType ) { - cameraVR.near = cameraR.near = cameraL.near = camera.near; - cameraVR.far = cameraR.far = cameraL.far = camera.far; + glInternalFormat = 36012; - if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { + } else if ( texture.type === UnsignedIntType ) { - // Note that the new renderState won't apply until the next frame. See #18320 + glInternalFormat = 33190; - session.updateRenderState( { - depthNear: cameraVR.near, - depthFar: cameraVR.far - } ); + } else if ( texture.type === UnsignedInt248Type ) { - _currentDepthNear = cameraVR.near; - _currentDepthFar = cameraVR.far; + glInternalFormat = 35056; - } + } else { - const parent = camera.parent; - const cameras = cameraVR.cameras; + glInternalFormat = 33189; // WebGL2 requires sized internalformat for glTexImage2D - updateCamera( cameraVR, parent ); + } - for ( let i = 0; i < cameras.length; i ++ ) { + } else { - updateCamera( cameras[ i ], parent ); + if ( texture.type === FloatType ) { - } + console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); - // update camera and its children + } - camera.matrixWorld.copy( cameraVR.matrixWorld ); - camera.matrix.copy( cameraVR.matrix ); - camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + } - const children = camera.children; + // validation checks for WebGL 1 - for ( let i = 0, l = children.length; i < l; i ++ ) { + if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { - children[ i ].updateMatrixWorld( true ); + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - } + console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - // update projection matrix for proper view frustum culling + texture.type = UnsignedShortType; + glType = utils.convert( texture.type ); - if ( cameras.length === 2 ) { + } - setProjectionFromUnion( cameraVR, cameraL, cameraR ); + } - } else { + if ( texture.format === DepthStencilFormat && glInternalFormat === 6402 ) { - // assume single camera setup (AR) + // Depth stencil textures need the DEPTH_STENCIL internal format + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + glInternalFormat = 34041; - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedInt248Type ) { - } + console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); - return cameraVR; + texture.type = UnsignedInt248Type; + glType = utils.convert( texture.type ); - }; + } - // Animation Loop + } - let onAnimationFrameCallback = null; + // - function onAnimationFrame( time, frame ) { + if ( useTexStorage && allocateMemory ) { - pose = frame.getViewerPose( referenceSpace ); + state.texStorage2D( 3553, 1, glInternalFormat, image.width, image.height ); - if ( pose !== null ) { + } else { - const views = pose.views; - const baseLayer = session.renderState.baseLayer; + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - state.bindXRFramebuffer( baseLayer.framebuffer ); + } - let cameraVRNeedsUpdate = false; + } else if ( texture.isDataTexture ) { - // check if it's necessary to rebuild cameraVR's camera list + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - if ( views.length !== cameraVR.cameras.length ) { + if ( mipmaps.length > 0 && supportsMips ) { - cameraVR.cameras.length = 0; - cameraVRNeedsUpdate = true; + if ( useTexStorage && allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - for ( let i = 0; i < views.length; i ++ ) { + } - const view = views[ i ]; - const viewport = baseLayer.getViewport( view ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - const camera = cameras[ i ]; - camera.matrix.fromArray( view.transform.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); + mipmap = mipmaps[ i ]; - if ( i === 0 ) { + if ( useTexStorage ) { - cameraVR.matrix.copy( camera.matrix ); + state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - } + } else { - if ( cameraVRNeedsUpdate === true ) { + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - cameraVR.cameras.push( camera ); + } } - } + texture.generateMipmaps = false; - } + } else { - // + if ( useTexStorage ) { - const inputSources = session.inputSources; + if ( allocateMemory ) { - for ( let i = 0; i < controllers.length; i ++ ) { + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - const controller = controllers[ i ]; - const inputSource = inputSources[ i ]; + } - controller.update( inputSource, frame, referenceSpace ); + state.texSubImage2D( 3553, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); - } + } else { - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - } + } - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + } - this.setAnimationLoop = function ( callback ) { + } else if ( texture.isCompressedTexture ) { - onAnimationFrameCallback = callback; + if ( useTexStorage && allocateMemory ) { - }; + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - this.dispose = function () {}; + } - } + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { -} + mipmap = mipmaps[ i ]; -function WebGLMaterials( properties ) { + if ( texture.format !== RGBAFormat ) { - function refreshFogUniforms( uniforms, fog ) { + if ( glFormat !== null ) { - uniforms.fogColor.value.copy( fog.color ); + if ( useTexStorage ) { - if ( fog.isFog ) { + state.compressedTexSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } else { - } else if ( fog.isFogExp2 ) { + state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - uniforms.fogDensity.value = fog.density; + } - } + } else { - } + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - function refreshMaterialUniforms( uniforms, material, pixelRatio, height ) { + } - if ( material.isMeshBasicMaterial ) { + } else { - refreshUniformsCommon( uniforms, material ); + if ( useTexStorage ) { - } else if ( material.isMeshLambertMaterial ) { + state.texSubImage2D( 3553, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsLambert( uniforms, material ); + } else { - } else if ( material.isMeshToonMaterial ) { + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsToon( uniforms, material ); + } - } else if ( material.isMeshPhongMaterial ) { + } - refreshUniformsCommon( uniforms, material ); - refreshUniformsPhong( uniforms, material ); + } - } else if ( material.isMeshStandardMaterial ) { + } else if ( texture.isDataArrayTexture ) { - refreshUniformsCommon( uniforms, material ); + if ( useTexStorage ) { - if ( material.isMeshPhysicalMaterial ) { + if ( allocateMemory ) { - refreshUniformsPhysical( uniforms, material ); + state.texStorage3D( 35866, levels, glInternalFormat, image.width, image.height, image.depth ); - } else { + } - refreshUniformsStandard( uniforms, material ); + state.texSubImage3D( 35866, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - } + } else { - } else if ( material.isMeshMatcapMaterial ) { + state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - refreshUniformsCommon( uniforms, material ); - refreshUniformsMatcap( uniforms, material ); + } - } else if ( material.isMeshDepthMaterial ) { + } else if ( texture.isData3DTexture ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsDepth( uniforms, material ); + if ( useTexStorage ) { - } else if ( material.isMeshDistanceMaterial ) { + if ( allocateMemory ) { - refreshUniformsCommon( uniforms, material ); - refreshUniformsDistance( uniforms, material ); + state.texStorage3D( 32879, levels, glInternalFormat, image.width, image.height, image.depth ); - } else if ( material.isMeshNormalMaterial ) { + } - refreshUniformsCommon( uniforms, material ); - refreshUniformsNormal( uniforms, material ); + state.texSubImage3D( 32879, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - } else if ( material.isLineBasicMaterial ) { + } else { - refreshUniformsLine( uniforms, material ); + state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - if ( material.isLineDashedMaterial ) { + } - refreshUniformsDash( uniforms, material ); + } else if ( texture.isFramebufferTexture ) { - } + if ( useTexStorage && allocateMemory ) { - } else if ( material.isPointsMaterial ) { + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - refreshUniformsPoints( uniforms, material, pixelRatio, height ); + } else { - } else if ( material.isSpriteMaterial ) { + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); - refreshUniformsSprites( uniforms, material ); + } - } else if ( material.isShadowMaterial ) { + } else { - uniforms.color.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + // regular Texture (image, video, canvas) - } else if ( material.isShaderMaterial ) { + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - material.uniformsNeedUpdate = false; // #15581 + if ( mipmaps.length > 0 && supportsMips ) { - } + if ( useTexStorage && allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - function refreshUniformsCommon( uniforms, material ) { + } - uniforms.opacity.value = material.opacity; + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - if ( material.color ) { + mipmap = mipmaps[ i ]; - uniforms.diffuse.value.copy( material.color ); + if ( useTexStorage ) { - } + state.texSubImage2D( 3553, i, 0, 0, glFormat, glType, mipmap ); - if ( material.emissive ) { + } else { - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); + state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); - } + } - if ( material.map ) { + } - uniforms.map.value = material.map; + texture.generateMipmaps = false; - } + } else { - if ( material.alphaMap ) { + if ( useTexStorage ) { - uniforms.alphaMap.value = material.alphaMap; + if ( allocateMemory ) { - } + state.texStorage2D( 3553, levels, glInternalFormat, image.width, image.height ); - if ( material.specularMap ) { + } - uniforms.specularMap.value = material.specularMap; + state.texSubImage2D( 3553, 0, 0, 0, glFormat, glType, image ); - } + } else { - const envMap = properties.get( material ).envMap; + state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - if ( envMap ) { + } - uniforms.envMap.value = envMap; + } - uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap._needsFlipEnvMap ) ? - 1 : 1; + } - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - const maxMipLevel = properties.get( envMap ).__maxMipLevel; + generateMipmap( textureType ); - if ( maxMipLevel !== undefined ) { + } - uniforms.maxMipLevel.value = maxMipLevel; + source.__currentVersion = source.version; - } + if ( texture.onUpdate ) texture.onUpdate( texture ); } - if ( material.lightMap ) { - - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; + textureProperties.__version = texture.version; - } + } - if ( material.aoMap ) { + function uploadCubeTexture( textureProperties, texture, slot ) { - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; + if ( texture.image.length !== 6 ) return; - } + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. displacementMap map - // 4. normal map - // 5. bump map - // 6. roughnessMap map - // 7. metalnessMap map - // 8. alphaMap map - // 9. emissiveMap map - // 10. clearcoat map - // 11. clearcoat normal map - // 12. clearcoat roughnessMap map + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); - let uvScaleMap; + if ( source.version !== source.__currentVersion || forceUpload === true ) { - if ( material.map ) { + _gl.pixelStorei( 37440, texture.flipY ); + _gl.pixelStorei( 37441, texture.premultiplyAlpha ); + _gl.pixelStorei( 3317, texture.unpackAlignment ); + _gl.pixelStorei( 37443, 0 ); - uvScaleMap = material.map; + const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); + const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - } else if ( material.specularMap ) { + const cubeImage = []; - uvScaleMap = material.specularMap; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.displacementMap ) { + if ( ! isCompressed && ! isDataTexture ) { - uvScaleMap = material.displacementMap; + cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - } else if ( material.normalMap ) { + } else { - uvScaleMap = material.normalMap; + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - } else if ( material.bumpMap ) { + } - uvScaleMap = material.bumpMap; + cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); - } else if ( material.roughnessMap ) { + } - uvScaleMap = material.roughnessMap; + const image = cubeImage[ 0 ], + supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.encoding ), + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); - } else if ( material.metalnessMap ) { + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( textureProperties.__version === undefined ); + let levels = getMipLevels( texture, image, supportsMips ); - uvScaleMap = material.metalnessMap; + setTextureParameters( 34067, texture, supportsMips ); - } else if ( material.alphaMap ) { + let mipmaps; - uvScaleMap = material.alphaMap; + if ( isCompressed ) { - } else if ( material.emissiveMap ) { + if ( useTexStorage && allocateMemory ) { - uvScaleMap = material.emissiveMap; + state.texStorage2D( 34067, levels, glInternalFormat, image.width, image.height ); - } else if ( material.clearcoatMap ) { + } - uvScaleMap = material.clearcoatMap; + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.clearcoatNormalMap ) { + mipmaps = cubeImage[ i ].mipmaps; - uvScaleMap = material.clearcoatNormalMap; + for ( let j = 0; j < mipmaps.length; j ++ ) { - } else if ( material.clearcoatRoughnessMap ) { + const mipmap = mipmaps[ j ]; - uvScaleMap = material.clearcoatRoughnessMap; + if ( texture.format !== RGBAFormat ) { - } + if ( glFormat !== null ) { - if ( uvScaleMap !== undefined ) { + if ( useTexStorage ) { - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { + state.compressedTexSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - uvScaleMap = uvScaleMap.texture; + } else { - } + state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - if ( uvScaleMap.matrixAutoUpdate === true ) { + } - uvScaleMap.updateMatrix(); + } else { - } + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } - } + } else { - // uv repeat and offset setting priorities for uv2 - // 1. ao map - // 2. light map + if ( useTexStorage ) { - let uv2ScaleMap; + state.texSubImage2D( 34069 + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - if ( material.aoMap ) { + } else { - uv2ScaleMap = material.aoMap; + state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } else if ( material.lightMap ) { + } - uv2ScaleMap = material.lightMap; + } - } + } - if ( uv2ScaleMap !== undefined ) { + } - // backwards compatibility - if ( uv2ScaleMap.isWebGLRenderTarget ) { + } else { - uv2ScaleMap = uv2ScaleMap.texture; + mipmaps = texture.mipmaps; - } + if ( useTexStorage && allocateMemory ) { - if ( uv2ScaleMap.matrixAutoUpdate === true ) { + // TODO: Uniformly handle mipmap definitions + // Normal textures and compressed cube textures define base level + mips with their mipmap array + // Uncompressed cube textures use their mipmap array only for mips (no base level) - uv2ScaleMap.updateMatrix(); + if ( mipmaps.length > 0 ) levels ++; - } + state.texStorage2D( 34067, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); - uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); + } - } + for ( let i = 0; i < 6; i ++ ) { - } + if ( isDataTexture ) { - function refreshUniformsLine( uniforms, material ) { + if ( useTexStorage ) { - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + state.texSubImage2D( 34069 + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); - } + } else { - function refreshUniformsDash( uniforms, material ) { + state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + } - } + for ( let j = 0; j < mipmaps.length; j ++ ) { - function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { + const mipmap = mipmaps[ j ]; + const mipmapImage = mipmap.image[ i ].image; - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * pixelRatio; - uniforms.scale.value = height * 0.5; + if ( useTexStorage ) { - if ( material.map ) { + state.texSubImage2D( 34069 + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); - uniforms.map.value = material.map; + } else { - } + state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - if ( material.alphaMap ) { + } - uniforms.alphaMap.value = material.alphaMap; + } - } + } else { - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + if ( useTexStorage ) { - let uvScaleMap; + state.texSubImage2D( 34069 + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); - if ( material.map ) { + } else { - uvScaleMap = material.map; + state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - } else if ( material.alphaMap ) { + } - uvScaleMap = material.alphaMap; + for ( let j = 0; j < mipmaps.length; j ++ ) { - } + const mipmap = mipmaps[ j ]; - if ( uvScaleMap !== undefined ) { + if ( useTexStorage ) { - if ( uvScaleMap.matrixAutoUpdate === true ) { + state.texSubImage2D( 34069 + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); - uvScaleMap.updateMatrix(); + } else { - } + state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); + } - } + } - } + } - function refreshUniformsSprites( uniforms, material ) { + } - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; + } - if ( material.map ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - uniforms.map.value = material.map; + // We assume images for cube map have the same size. + generateMipmap( 34067 ); - } + } - if ( material.alphaMap ) { + source.__currentVersion = source.version; - uniforms.alphaMap.value = material.alphaMap; + if ( texture.onUpdate ) texture.onUpdate( texture ); } - // uv repeat and offset setting priorities - // 1. color map - // 2. alpha map + textureProperties.__version = texture.version; - let uvScaleMap; + } - if ( material.map ) { + // Render targets - uvScaleMap = material.map; + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) { - } else if ( material.alphaMap ) { + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const renderTargetProperties = properties.get( renderTarget ); - uvScaleMap = material.alphaMap; + if ( ! renderTargetProperties.__hasExternalTextures ) { - } + if ( textureTarget === 32879 || textureTarget === 35866 ) { - if ( uvScaleMap !== undefined ) { + state.texImage3D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null ); - if ( uvScaleMap.matrixAutoUpdate === true ) { + } else { - uvScaleMap.updateMatrix(); + state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); } - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - } - } + state.bindFramebuffer( 36160, framebuffer ); - function refreshUniformsLambert( uniforms, material ) { + if ( useMultisampledRTT( renderTarget ) ) { - if ( material.emissiveMap ) { + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); - uniforms.emissiveMap.value = material.emissiveMap; + } else { + + _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( texture ).__webglTexture, 0 ); } + state.bindFramebuffer( 36160, null ); + } - function refreshUniformsPhong( uniforms, material ) { - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - if ( material.emissiveMap ) { + _gl.bindRenderbuffer( 36161, renderbuffer ); - uniforms.emissiveMap.value = material.emissiveMap; + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - } + let glInternalFormat = 33189; - if ( material.bumpMap ) { + if ( isMultisample || useMultisampledRTT( renderTarget ) ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const depthTexture = renderTarget.depthTexture; - } + if ( depthTexture && depthTexture.isDepthTexture ) { - if ( material.normalMap ) { + if ( depthTexture.type === FloatType ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + glInternalFormat = 36012; - } + } else if ( depthTexture.type === UnsignedIntType ) { - if ( material.displacementMap ) { + glInternalFormat = 33190; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } - } + } - } + const samples = getRenderTargetSamples( renderTarget ); - function refreshUniformsToon( uniforms, material ) { + if ( useMultisampledRTT( renderTarget ) ) { - if ( material.gradientMap ) { + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - uniforms.gradientMap.value = material.gradientMap; + } else { - } + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.emissiveMap ) { + } - uniforms.emissiveMap.value = material.emissiveMap; + } else { - } + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.bumpMap ) { + } - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); - } + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - if ( material.normalMap ) { + const samples = getRenderTargetSamples( renderTarget ); - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - } + _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - if ( material.displacementMap ) { + } else if ( useMultisampledRTT( renderTarget ) ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, 35056, renderTarget.width, renderTarget.height ); - } + } else { - } + _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); - function refreshUniformsStandard( uniforms, material ) { + } - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; - if ( material.roughnessMap ) { + _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); - uniforms.roughnessMap.value = material.roughnessMap; + } else { - } + // Use the first texture for MRT so far + const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[ 0 ] : renderTarget.texture; - if ( material.metalnessMap ) { + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const samples = getRenderTargetSamples( renderTarget ); - uniforms.metalnessMap.value = material.metalnessMap; + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - } + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.emissiveMap ) { + } else if ( useMultisampledRTT( renderTarget ) ) { - uniforms.emissiveMap.value = material.emissiveMap; + multisampledRTTExt.renderbufferStorageMultisampleEXT( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - } + } else { - if ( material.bumpMap ) { + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } } - if ( material.normalMap ) { + _gl.bindRenderbuffer( 36161, null ); - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + } - } + // Setup resources for a Depth Texture for a FBO (needs an extension) + function setupDepthTexture( framebuffer, renderTarget ) { - if ( material.displacementMap ) { + const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); + if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + state.bindFramebuffer( 36160, framebuffer ); - } + if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - const envMap = properties.get( material ).envMap; + throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); - if ( envMap ) { + } - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; + // upload an empty depth texture with framebuffer size + if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || + renderTarget.depthTexture.image.width !== renderTarget.width || + renderTarget.depthTexture.image.height !== renderTarget.height ) { + + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; } - } + setTexture2D( renderTarget.depthTexture, 0 ); - function refreshUniformsPhysical( uniforms, material ) { + const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + const samples = getRenderTargetSamples( renderTarget ); - refreshUniformsStandard( uniforms, material ); + if ( renderTarget.depthTexture.format === DepthFormat ) { - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + if ( useMultisampledRTT( renderTarget ) ) { - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 36096, 3553, webglDepthTexture, 0, samples ); - if ( material.clearcoatMap ) { + } else { - uniforms.clearcoatMap.value = material.clearcoatMap; + _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); - } + } - if ( material.clearcoatRoughnessMap ) { + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; + if ( useMultisampledRTT( renderTarget ) ) { - } + multisampledRTTExt.framebufferTexture2DMultisampleEXT( 36160, 33306, 3553, webglDepthTexture, 0, samples ); - if ( material.clearcoatNormalMap ) { + } else { - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; + _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); - if ( material.side === BackSide ) { + } - uniforms.clearcoatNormalScale.value.negate(); + } else { - } + throw new Error( 'Unknown depthTexture format' ); } - uniforms.transmission.value = material.transmission; + } - if ( material.transmissionMap ) { + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { - uniforms.transmissionMap.value = material.transmissionMap; + const renderTargetProperties = properties.get( renderTarget ); + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - } + if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { - } + if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - function refreshUniformsMatcap( uniforms, material ) { + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - if ( material.matcap ) { + } else { - uniforms.matcap.value = material.matcap; + if ( isCube ) { - } + renderTargetProperties.__webglDepthbuffer = []; - if ( material.bumpMap ) { + for ( let i = 0; i < 6; i ++ ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - } + } - if ( material.normalMap ) { + } else { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + state.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); + + } } - if ( material.displacementMap ) { + state.bindFramebuffer( 36160, null ); - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } - } + // rebind framebuffer with external textures + function rebindTextures( renderTarget, colorTexture, depthTexture ) { - } + const renderTargetProperties = properties.get( renderTarget ); - function refreshUniformsDepth( uniforms, material ) { + if ( colorTexture !== undefined ) { - if ( material.displacementMap ) { + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, 36064, 3553 ); - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + } + + if ( depthTexture !== undefined ) { + + setupDepthRenderbuffer( renderTarget ); } } - function refreshUniformsDistance( uniforms, material ) { + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { - if ( material.displacementMap ) { + const texture = renderTarget.texture; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - } + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; + if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { - } + if ( textureProperties.__webglTexture === undefined ) { - function refreshUniformsNormal( uniforms, material ) { + textureProperties.__webglTexture = _gl.createTexture(); - if ( material.bumpMap ) { + } - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + textureProperties.__version = texture.version; + info.memory.textures ++; } - if ( material.normalMap ) { + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + // Setup framebuffer - } + if ( isCube ) { - if ( material.displacementMap ) { + renderTargetProperties.__webglFramebuffer = []; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + for ( let i = 0; i < 6; i ++ ) { - } + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - } + } - return { - refreshFogUniforms: refreshFogUniforms, - refreshMaterialUniforms: refreshMaterialUniforms - }; + } else { -} + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); -function createCanvasElement() { + if ( isMultipleRenderTargets ) { - const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.style.display = 'block'; - return canvas; + if ( capabilities.drawBuffers ) { -} + const textures = renderTarget.texture; -function WebGLRenderer( parameters ) { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - parameters = parameters || {}; + const attachmentProperties = properties.get( textures[ i ] ); - const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), - _context = parameters.context !== undefined ? parameters.context : null, + if ( attachmentProperties.__webglTexture === undefined ) { - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; + attachmentProperties.__webglTexture = _gl.createTexture(); - let currentRenderList = null; - let currentRenderState = null; + info.memory.textures ++; - // render() can be called from within a callback triggered by another render. - // We track this so that the nested render call gets its list and state isolated from the parent render call. + } - const renderListStack = []; - const renderStateStack = []; + } - // public properties + } else { - this.domElement = _canvas; + console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' ); - // Debug configuration container - this.debug = { + } - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; + } else if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - // clearing + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - // scene graph + const glFormat = utils.convert( texture.format, texture.encoding ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + const samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - this.sortObjects = true; + state.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); + _gl.bindRenderbuffer( 36161, null ); - // user-defined clipping + if ( renderTarget.depthBuffer ) { - this.clippingPlanes = []; - this.localClippingEnabled = false; + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - // physically based shading + } - this.gammaFactor = 2.0; // for backwards compatibility - this.outputEncoding = LinearEncoding; + state.bindFramebuffer( 36160, null ); - // physical lights + } - this.physicallyCorrectLights = false; + } - // tone mapping + // Setup color buffer - this.toneMapping = NoToneMapping; - this.toneMappingExposure = 1.0; + if ( isCube ) { - // internal properties + state.bindTexture( 34067, textureProperties.__webglTexture ); + setTextureParameters( 34067, texture, supportsMips ); - const _this = this; + for ( let i = 0; i < 6; i ++ ) { - let _isContextLost = false; + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, 36064, 34069 + i ); - // internal state cache + } - let _currentActiveCubeFace = 0; - let _currentActiveMipmapLevel = 0; - let _currentRenderTarget = null; - let _currentMaterialId = - 1; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - let _currentCamera = null; + generateMipmap( 34067 ); - const _currentViewport = new Vector4(); - const _currentScissor = new Vector4(); - let _currentScissorTest = null; + } - // + state.unbindTexture(); - let _width = _canvas.width; - let _height = _canvas.height; + } else if ( isMultipleRenderTargets ) { - let _pixelRatio = 1; - let _opaqueSort = null; - let _transparentSort = null; + const textures = renderTarget.texture; - const _viewport = new Vector4( 0, 0, _width, _height ); - const _scissor = new Vector4( 0, 0, _width, _height ); - let _scissorTest = false; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - // frustum + const attachment = textures[ i ]; + const attachmentProperties = properties.get( attachment ); - const _frustum = new Frustum(); + state.bindTexture( 3553, attachmentProperties.__webglTexture ); + setTextureParameters( 3553, attachment, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, 36064 + i, 3553 ); - // clipping + if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { - let _clippingEnabled = false; - let _localClippingEnabled = false; + generateMipmap( 3553 ); - // camera matrices cache + } - const _projScreenMatrix = new Matrix4(); + } - const _vector3 = new Vector3(); + state.unbindTexture(); - const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; + } else { - function getTargetPixelRatio() { + let glTextureType = 3553; - return _currentRenderTarget === null ? _pixelRatio : 1; + if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { - } + if ( isWebGL2 ) { - // initialize + glTextureType = renderTarget.isWebGL3DRenderTarget ? 32879 : 35866; - let _gl = _context; + } else { - function getContext( contextNames, contextAttributes ) { + console.error( 'THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.' ); - for ( let i = 0; i < contextNames.length; i ++ ) { + } - const contextName = contextNames[ i ]; - const context = _canvas.getContext( contextName, contextAttributes ); - if ( context !== null ) return context; + } - } + state.bindTexture( glTextureType, textureProperties.__webglTexture ); + setTextureParameters( glTextureType, texture, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, 36064, glTextureType ); - return null; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - } + generateMipmap( glTextureType ); - try { + } - const contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat - }; + state.unbindTexture(); - // event listeners must be registered before WebGL context is created, see #12753 + } - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + // Setup depth and stencil buffers - if ( _gl === null ) { + if ( renderTarget.depthBuffer ) { - const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; + setupDepthRenderbuffer( renderTarget ); - if ( _this.isWebGL1Renderer === true ) { + } - contextNames.shift(); + } - } + function updateRenderTargetMipmap( renderTarget ) { - _gl = getContext( contextNames, contextAttributes ); + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - if ( _gl === null ) { + const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - if ( getContext( contextNames ) ) { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - throw new Error( 'Error creating WebGL context with your selected attributes.' ); + const texture = textures[ i ]; - } else { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - throw new Error( 'Error creating WebGL context.' ); + const target = renderTarget.isWebGLCubeRenderTarget ? 34067 : 3553; + const webglTexture = properties.get( texture ).__webglTexture; - } + state.bindTexture( target, webglTexture ); + generateMipmap( target ); + state.unbindTexture(); } } - // Some experimental-webgl implementations do not have getShaderPrecisionFormat - - if ( _gl.getShaderPrecisionFormat === undefined ) { + } - _gl.getShaderPrecisionFormat = function () { + function updateMultisampleRenderTarget( renderTarget ) { - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - }; + const width = renderTarget.width; + const height = renderTarget.height; + let mask = 16384; + const invalidationArray = [ 36064 ]; + const depthStyle = renderTarget.stencilBuffer ? 33306 : 36096; - } + if ( renderTarget.depthBuffer ) { - } catch ( error ) { + invalidationArray.push( depthStyle ); - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; + } - } + const renderTargetProperties = properties.get( renderTarget ); + const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; - let extensions, capabilities, state, info; - let properties, textures, cubemaps, attributes, geometries, objects; - let programCache, materials, renderLists, renderStates, clipping, shadowMap; + if ( ignoreDepthValues === false ) { - let background, morphtargets, bufferRenderer, indexedBufferRenderer; + if ( renderTarget.depthBuffer ) mask |= 256; + if ( renderTarget.stencilBuffer ) mask |= 1024; - let utils, bindingStates; + } - function initGLContext() { + state.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); - extensions = new WebGLExtensions( _gl ); + if ( ignoreDepthValues === true ) { - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); + _gl.invalidateFramebuffer( 36008, [ depthStyle ] ); + _gl.invalidateFramebuffer( 36009, [ depthStyle ] ); - extensions.init( capabilities ); + } - utils = new WebGLUtils( _gl, extensions, capabilities ); + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); - state = new WebGLState( _gl, extensions, capabilities ); + if ( supportsInvalidateFramebuffer ) { - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - attributes = new WebGLAttributes( _gl, capabilities ); - bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); - geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); - morphtargets = new WebGLMorphtargets( _gl ); - clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, extensions, capabilities, bindingStates, clipping ); - materials = new WebGLMaterials( properties ); - renderLists = new WebGLRenderLists( properties ); - renderStates = new WebGLRenderStates( extensions, capabilities ); - background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha ); - shadowMap = new WebGLShadowMap( _this, objects, capabilities ); + _gl.invalidateFramebuffer( 36008, invalidationArray ); - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); + } - info.programs = programCache.programs; + state.bindFramebuffer( 36008, null ); + state.bindFramebuffer( 36009, renderTargetProperties.__webglMultisampledFramebuffer ); - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.shadowMap = shadowMap; - _this.state = state; - _this.info = info; + } } - initGLContext(); - - // xr + function getRenderTargetSamples( renderTarget ) { - const xr = new WebXRManager( _this, _gl ); + return Math.min( maxSamples, renderTarget.samples ); - this.xr = xr; + } - // API + function useMultisampledRTT( renderTarget ) { - this.getContext = function () { + const renderTargetProperties = properties.get( renderTarget ); - return _gl; + return isWebGL2 && renderTarget.samples > 0 && extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true && renderTargetProperties.__useRenderToTexture !== false; - }; + } - this.getContextAttributes = function () { + function updateVideoTexture( texture ) { - return _gl.getContextAttributes(); + const frame = info.render.frame; - }; + // Check the last frame we updated the VideoTexture - this.forceContextLoss = function () { + if ( _videoTextures.get( texture ) !== frame ) { - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); + _videoTextures.set( texture, frame ); + texture.update(); - }; + } - this.forceContextRestore = function () { + } - const extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); + function verifyColorSpace( texture, image ) { - }; + const encoding = texture.encoding; + const format = texture.format; + const type = texture.type; - this.getPixelRatio = function () { + if ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image; - return _pixelRatio; + if ( encoding !== LinearEncoding ) { - }; + // sRGB - this.setPixelRatio = function ( value ) { + if ( encoding === sRGBEncoding ) { - if ( value === undefined ) return; + if ( isWebGL2 === false ) { - _pixelRatio = value; + // in WebGL 1, try to use EXT_sRGB extension and unsized formats - this.setSize( _width, _height, false ); + if ( extensions.has( 'EXT_sRGB' ) === true && format === RGBAFormat ) { - }; + texture.format = _SRGBAFormat; - this.getSize = function ( target ) { + // it's not possible to generate mips in WebGL 1 with this extension - if ( target === undefined ) { + texture.minFilter = LinearFilter; + texture.generateMipmaps = false; - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); + } else { - target = new Vector2(); + // slow fallback (CPU decode) - } + image = ImageUtils.sRGBToLinear( image ); - return target.set( _width, _height ); + } - }; + } else { - this.setSize = function ( width, height, updateStyle ) { + // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format - if ( xr.isPresenting ) { + if ( format !== RGBAFormat || type !== UnsignedByteType ) { - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; + console.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); - } + } - _width = width; - _height = height; + } - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); + } else { - if ( updateStyle !== false ) { + console.error( 'THREE.WebGLTextures: Unsupported texture encoding:', encoding ); - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + } } - this.setViewport( 0, 0, width, height ); + return image; - }; + } - this.getDrawingBufferSize = function ( target ) { + // - if ( target === undefined ) { + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; + this.setTextureCube = setTextureCube; + this.rebindTextures = rebindTextures; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + this.setupDepthRenderbuffer = setupDepthRenderbuffer; + this.setupFrameBufferTexture = setupFrameBufferTexture; + this.useMultisampledRTT = useMultisampledRTT; - target = new Vector2(); +} - } +function WebGLUtils( gl, extensions, capabilities ) { - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); + const isWebGL2 = capabilities.isWebGL2; - }; + function convert( p, encoding = null ) { - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { + let extension; - _width = width; - _height = height; + if ( p === UnsignedByteType ) return 5121; + if ( p === UnsignedShort4444Type ) return 32819; + if ( p === UnsignedShort5551Type ) return 32820; - _pixelRatio = pixelRatio; + if ( p === ByteType ) return 5120; + if ( p === ShortType ) return 5122; + if ( p === UnsignedShortType ) return 5123; + if ( p === IntType ) return 5124; + if ( p === UnsignedIntType ) return 5125; + if ( p === FloatType ) return 5126; - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); + if ( p === HalfFloatType ) { - this.setViewport( 0, 0, width, height ); + if ( isWebGL2 ) return 5131; - }; + extension = extensions.get( 'OES_texture_half_float' ); - this.getCurrentViewport = function ( target ) { + if ( extension !== null ) { + + return extension.HALF_FLOAT_OES; - if ( target === undefined ) { + } else { - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); + return null; - target = new Vector4(); + } } - return target.copy( _currentViewport ); + if ( p === AlphaFormat ) return 6406; + if ( p === RGBAFormat ) return 6408; + if ( p === LuminanceFormat ) return 6409; + if ( p === LuminanceAlphaFormat ) return 6410; + if ( p === DepthFormat ) return 6402; + if ( p === DepthStencilFormat ) return 34041; + if ( p === RedFormat ) return 6403; - }; + if ( p === RGBFormat ) { - this.getViewport = function ( target ) { + console.warn( 'THREE.WebGLRenderer: THREE.RGBFormat has been removed. Use THREE.RGBAFormat instead. https://github.com/mrdoob/three.js/pull/23228' ); + return 6408; - return target.copy( _viewport ); + } - }; + // WebGL 1 sRGB fallback - this.setViewport = function ( x, y, width, height ) { + if ( p === _SRGBAFormat ) { - if ( x.isVector4 ) { + extension = extensions.get( 'EXT_sRGB' ); - _viewport.set( x.x, x.y, x.z, x.w ); + if ( extension !== null ) { - } else { + return extension.SRGB_ALPHA_EXT; - _viewport.set( x, y, width, height ); + } else { - } + return null; - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + } - }; + } - this.getScissor = function ( target ) { + // WebGL2 formats. - return target.copy( _scissor ); + if ( p === RedIntegerFormat ) return 36244; + if ( p === RGFormat ) return 33319; + if ( p === RGIntegerFormat ) return 33320; + if ( p === RGBAIntegerFormat ) return 36249; - }; + // S3TC - this.setScissor = function ( x, y, width, height ) { + if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - if ( x.isVector4 ) { + if ( encoding === sRGBEncoding ) { - _scissor.set( x.x, x.y, x.z, x.w ); + extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' ); - } else { + if ( extension !== null ) { - _scissor.set( x, y, width, height ); + if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - } + } else { - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); + return null; - }; + } - this.getScissorTest = function () { + } else { - return _scissorTest; + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - }; + if ( extension !== null ) { - this.setScissorTest = function ( boolean ) { + if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; - state.setScissorTest( _scissorTest = boolean ); + } else { - }; + return null; - this.setOpaqueSort = function ( method ) { + } - _opaqueSort = method; + } - }; + } - this.setTransparentSort = function ( method ) { + // PVRTC - _transparentSort = method; + if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - }; + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - // Clearing + if ( extension !== null ) { - this.getClearColor = function ( target ) { + if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; - if ( target === undefined ) { + } else { - console.warn( 'WebGLRenderer: .getClearColor() now requires a Color as an argument' ); + return null; - target = new Color(); + } } - return target.copy( background.getClearColor() ); - - }; - - this.setClearColor = function () { - - background.setClearColor.apply( background, arguments ); - - }; + // ETC1 - this.getClearAlpha = function () { + if ( p === RGB_ETC1_Format ) { - return background.getClearAlpha(); + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - }; + if ( extension !== null ) { - this.setClearAlpha = function () { + return extension.COMPRESSED_RGB_ETC1_WEBGL; - background.setClearAlpha.apply( background, arguments ); + } else { - }; + return null; - this.clear = function ( color, depth, stencil ) { + } - let bits = 0; + } - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; + // ETC2 - _gl.clear( bits ); + if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - }; + extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - this.clearColor = function () { + if ( extension !== null ) { - this.clear( true, false, false ); + if ( p === RGB_ETC2_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; + if ( p === RGBA_ETC2_EAC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; - }; + } else { - this.clearDepth = function () { + return null; - this.clear( false, true, false ); + } - }; + } - this.clearStencil = function () { + // ASTC - this.clear( false, false, true ); + if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || + p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || + p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || + p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || + p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { - }; + extension = extensions.get( 'WEBGL_compressed_texture_astc' ); - // + if ( extension !== null ) { - this.dispose = function () { + if ( p === RGBA_ASTC_4x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; + if ( p === RGBA_ASTC_5x4_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; + if ( p === RGBA_ASTC_5x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; + if ( p === RGBA_ASTC_6x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; + if ( p === RGBA_ASTC_6x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; + if ( p === RGBA_ASTC_8x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; + if ( p === RGBA_ASTC_8x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; + if ( p === RGBA_ASTC_8x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; + if ( p === RGBA_ASTC_10x5_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; + if ( p === RGBA_ASTC_10x6_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; + if ( p === RGBA_ASTC_10x8_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; + if ( p === RGBA_ASTC_10x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; + if ( p === RGBA_ASTC_12x10_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; + if ( p === RGBA_ASTC_12x12_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + } else { - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - cubemaps.dispose(); - objects.dispose(); - bindingStates.dispose(); + return null; - xr.dispose(); + } - xr.removeEventListener( 'sessionstart', onXRSessionStart ); - xr.removeEventListener( 'sessionend', onXRSessionEnd ); + } - animation.stop(); + // BPTC - }; + if ( p === RGBA_BPTC_Format ) { - // Events + extension = extensions.get( 'EXT_texture_compression_bptc' ); - function onContextLost( event ) { + if ( extension !== null ) { - event.preventDefault(); + if ( p === RGBA_BPTC_Format ) return ( encoding === sRGBEncoding ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + } else { - _isContextLost = true; + return null; - } + } - function onContextRestore( /* event */ ) { + } - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + // - _isContextLost = false; + if ( p === UnsignedInt248Type ) { - const infoAutoReset = info.autoReset; - const shadowMapEnabled = shadowMap.enabled; - const shadowMapAutoUpdate = shadowMap.autoUpdate; - const shadowMapNeedsUpdate = shadowMap.needsUpdate; - const shadowMapType = shadowMap.type; + if ( isWebGL2 ) return 34042; - initGLContext(); + extension = extensions.get( 'WEBGL_depth_texture' ); - info.autoReset = infoAutoReset; - shadowMap.enabled = shadowMapEnabled; - shadowMap.autoUpdate = shadowMapAutoUpdate; - shadowMap.needsUpdate = shadowMapNeedsUpdate; - shadowMap.type = shadowMapType; + if ( extension !== null ) { - } + return extension.UNSIGNED_INT_24_8_WEBGL; - function onMaterialDispose( event ) { + } else { - const material = event.target; + return null; - material.removeEventListener( 'dispose', onMaterialDispose ); + } - deallocateMaterial( material ); + } } - // Buffer deallocation + return { convert: convert }; - function deallocateMaterial( material ) { +} - releaseMaterialProgramReferences( material ); +class ArrayCamera extends PerspectiveCamera { - properties.remove( material ); + constructor( array = [] ) { - } + super(); + this.cameras = array; - function releaseMaterialProgramReferences( material ) { + } - const programs = properties.get( material ).programs; +} - if ( programs !== undefined ) { +ArrayCamera.prototype.isArrayCamera = true; - programs.forEach( function ( program ) { +class Group extends Object3D { - programCache.releaseProgram( program ); + constructor() { - } ); + super(); - } + this.type = 'Group'; } - // Buffer rendering - - function renderObjectImmediate( object, program ) { +} - object.render( function ( object ) { +Group.prototype.isGroup = true; - _this.renderBufferImmediate( object, program ); +const _moveEvent = { type: 'move' }; - } ); +class WebXRController { - } + constructor() { - this.renderBufferImmediate = function ( object, program ) { + this._targetRay = null; + this._grip = null; + this._hand = null; - bindingStates.initAttributes(); + } - const buffers = properties.get( object ); + getHandSpace() { - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + if ( this._hand === null ) { - const programAttributes = program.getAttributes(); + this._hand = new Group(); + this._hand.matrixAutoUpdate = false; + this._hand.visible = false; - if ( object.hasPositions ) { + this._hand.joints = {}; + this._hand.inputState = { pinching: false }; - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); + } - bindingStates.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); + return this._hand; - } + } - if ( object.hasNormals ) { + getTargetRaySpace() { - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); + if ( this._targetRay === null ) { - bindingStates.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); + this._targetRay = new Group(); + this._targetRay.matrixAutoUpdate = false; + this._targetRay.visible = false; + this._targetRay.hasLinearVelocity = false; + this._targetRay.linearVelocity = new Vector3(); + this._targetRay.hasAngularVelocity = false; + this._targetRay.angularVelocity = new Vector3(); } - if ( object.hasUvs ) { + return this._targetRay; - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); + } - bindingStates.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); + getGripSpace() { - } + if ( this._grip === null ) { - if ( object.hasColors ) { + this._grip = new Group(); + this._grip.matrixAutoUpdate = false; + this._grip.visible = false; + this._grip.hasLinearVelocity = false; + this._grip.linearVelocity = new Vector3(); + this._grip.hasAngularVelocity = false; + this._grip.angularVelocity = new Vector3(); - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); + } - bindingStates.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); + return this._grip; - } + } - bindingStates.disableUnusedAttributes(); + dispatchEvent( event ) { - _gl.drawArrays( 4, 0, object.count ); + if ( this._targetRay !== null ) { - object.count = 0; + this._targetRay.dispatchEvent( event ); - }; + } - this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { + if ( this._grip !== null ) { - if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) + this._grip.dispatchEvent( event ); - const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); + } - const program = setProgram( camera, scene, material, object ); + if ( this._hand !== null ) { - state.setMaterial( material, frontFaceCW ); + this._hand.dispatchEvent( event ); - // + } - let index = geometry.index; - const position = geometry.attributes.position; + return this; - // + } - if ( index === null ) { + disconnect( inputSource ) { - if ( position === undefined || position.count === 0 ) return; + this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - } else if ( index.count === 0 ) { + if ( this._targetRay !== null ) { - return; + this._targetRay.visible = false; } - // + if ( this._grip !== null ) { - let rangeFactor = 1; + this._grip.visible = false; - if ( material.wireframe === true ) { + } - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; + if ( this._hand !== null ) { + + this._hand.visible = false; } - if ( material.morphTargets || material.morphNormals ) { + return this; - morphtargets.update( object, geometry, material, program ); + } - } + update( inputSource, frame, referenceSpace ) { - bindingStates.setup( object, material, program, geometry, index ); + let inputPose = null; + let gripPose = null; + let handPose = null; - let attribute; - let renderer = bufferRenderer; + const targetRay = this._targetRay; + const grip = this._grip; + const hand = this._hand; - if ( index !== null ) { + if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { - attribute = attributes.get( index ); + if ( targetRay !== null ) { - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); + inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - } + if ( inputPose !== null ) { - // + targetRay.matrix.fromArray( inputPose.transform.matrix ); + targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); - const dataCount = ( index !== null ) ? index.count : position.count; + if ( inputPose.linearVelocity ) { - const rangeStart = geometry.drawRange.start * rangeFactor; - const rangeCount = geometry.drawRange.count * rangeFactor; + targetRay.hasLinearVelocity = true; + targetRay.linearVelocity.copy( inputPose.linearVelocity ); - const groupStart = group !== null ? group.start * rangeFactor : 0; - const groupCount = group !== null ? group.count * rangeFactor : Infinity; + } else { - const drawStart = Math.max( rangeStart, groupStart ); - const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + targetRay.hasLinearVelocity = false; - const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + } - if ( drawCount === 0 ) return; + if ( inputPose.angularVelocity ) { - // + targetRay.hasAngularVelocity = true; + targetRay.angularVelocity.copy( inputPose.angularVelocity ); - if ( object.isMesh ) { + } else { - if ( material.wireframe === true ) { + targetRay.hasAngularVelocity = false; - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); + } - } else { + this.dispatchEvent( _moveEvent ); - renderer.setMode( 4 ); + } } - } else if ( object.isLine ) { + if ( hand && inputSource.hand ) { - let lineWidth = material.linewidth; + handPose = true; - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + for ( const inputjoint of inputSource.hand.values() ) { - state.setLineWidth( lineWidth * getTargetPixelRatio() ); + // Update the joints groups with the XRJoint poses + const jointPose = frame.getJointPose( inputjoint, referenceSpace ); - if ( object.isLineSegments ) { + if ( hand.joints[ inputjoint.jointName ] === undefined ) { - renderer.setMode( 1 ); + // The transform of this joint will be updated with the joint pose on each frame + const joint = new Group(); + joint.matrixAutoUpdate = false; + joint.visible = false; + hand.joints[ inputjoint.jointName ] = joint; + // ?? + hand.add( joint ); - } else if ( object.isLineLoop ) { + } - renderer.setMode( 2 ); + const joint = hand.joints[ inputjoint.jointName ]; - } else { + if ( jointPose !== null ) { - renderer.setMode( 3 ); + joint.matrix.fromArray( jointPose.transform.matrix ); + joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); + joint.jointRadius = jointPose.radius; - } + } - } else if ( object.isPoints ) { + joint.visible = jointPose !== null; - renderer.setMode( 0 ); + } - } else if ( object.isSprite ) { + // Custom events - renderer.setMode( 4 ); + // Check pinchz + const indexTip = hand.joints[ 'index-finger-tip' ]; + const thumbTip = hand.joints[ 'thumb-tip' ]; + const distance = indexTip.position.distanceTo( thumbTip.position ); - } + const distanceToPinch = 0.02; + const threshold = 0.005; - if ( object.isInstancedMesh ) { + if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - renderer.renderInstances( drawStart, drawCount, object.count ); + hand.inputState.pinching = false; + this.dispatchEvent( { + type: 'pinchend', + handedness: inputSource.handedness, + target: this + } ); - } else if ( geometry.isInstancedBufferGeometry ) { + } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); + hand.inputState.pinching = true; + this.dispatchEvent( { + type: 'pinchstart', + handedness: inputSource.handedness, + target: this + } ); - renderer.renderInstances( drawStart, drawCount, instanceCount ); + } - } else { + } else { - renderer.render( drawStart, drawCount ); + if ( grip !== null && inputSource.gripSpace ) { - } + gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - }; + if ( gripPose !== null ) { - // Compile + grip.matrix.fromArray( gripPose.transform.matrix ); + grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); - this.compile = function ( scene, camera ) { + if ( gripPose.linearVelocity ) { - currentRenderState = renderStates.get( scene ); - currentRenderState.init(); + grip.hasLinearVelocity = true; + grip.linearVelocity.copy( gripPose.linearVelocity ); - scene.traverseVisible( function ( object ) { + } else { - if ( object.isLight && object.layers.test( camera.layers ) ) { + grip.hasLinearVelocity = false; - currentRenderState.pushLight( object ); + } - if ( object.castShadow ) { + if ( gripPose.angularVelocity ) { - currentRenderState.pushShadow( object ); + grip.hasAngularVelocity = true; + grip.angularVelocity.copy( gripPose.angularVelocity ); + + } else { + + grip.hasAngularVelocity = false; + + } + + } } } - } ); + } - currentRenderState.setupLights(); + if ( targetRay !== null ) { - scene.traverse( function ( object ) { + targetRay.visible = ( inputPose !== null ); - const material = object.material; + } - if ( material ) { + if ( grip !== null ) { - if ( Array.isArray( material ) ) { + grip.visible = ( gripPose !== null ); - for ( let i = 0; i < material.length; i ++ ) { + } - const material2 = material[ i ]; + if ( hand !== null ) { - getProgram( material2, scene, object ); + hand.visible = ( handPose !== null ); - } + } - } else { + return this; - getProgram( material, scene, object ); + } - } +} - } +class DepthTexture extends Texture { - } ); + constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - }; + format = format !== undefined ? format : DepthFormat; - // Animation Loop + if ( format !== DepthFormat && format !== DepthStencilFormat ) { - let onAnimationFrameCallback = null; + throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - function onAnimationFrame( time ) { + } - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; + if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - } + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - function onXRSessionStart() { + this.image = { width: width, height: height }; - animation.stop(); + this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - } + this.flipY = false; + this.generateMipmaps = false; - function onXRSessionEnd() { + } - animation.start(); - } +} - const animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); +DepthTexture.prototype.isDepthTexture = true; - if ( typeof window !== 'undefined' ) animation.setContext( window ); +class WebXRManager extends EventDispatcher$1 { - this.setAnimationLoop = function ( callback ) { + constructor( renderer, gl ) { - onAnimationFrameCallback = callback; - xr.setAnimationLoop( callback ); + super(); - ( callback === null ) ? animation.stop() : animation.start(); + const scope = this; - }; + let session = null; + let framebufferScaleFactor = 1.0; - xr.addEventListener( 'sessionstart', onXRSessionStart ); - xr.addEventListener( 'sessionend', onXRSessionEnd ); + let referenceSpace = null; + let referenceSpaceType = 'local-floor'; - // Rendering + let pose = null; + let glBinding = null; + let glProjLayer = null; + let glBaseLayer = null; + let xrFrame = null; + const attributes = gl.getContextAttributes(); + let initialRenderTarget = null; + let newRenderTarget = null; - this.render = function ( scene, camera ) { + const controllers = []; + const inputSourcesMap = new Map(); - let renderTarget, forceClear; + // - if ( arguments[ 2 ] !== undefined ) { + const cameraL = new PerspectiveCamera(); + cameraL.layers.enable( 1 ); + cameraL.viewport = new Vector4(); - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; + const cameraR = new PerspectiveCamera(); + cameraR.layers.enable( 2 ); + cameraR.viewport = new Vector4(); - } + const cameras = [ cameraL, cameraR ]; - if ( arguments[ 3 ] !== undefined ) { + const cameraVR = new ArrayCamera(); + cameraVR.layers.enable( 1 ); + cameraVR.layers.enable( 2 ); - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; + let _currentDepthNear = null; + let _currentDepthFar = null; - } + // - if ( camera !== undefined && camera.isCamera !== true ) { + this.cameraAutoUpdate = true; + this.enabled = false; - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + this.isPresenting = false; - } + this.getController = function ( index ) { - if ( _isContextLost === true ) return; + let controller = controllers[ index ]; - // update scene graph + if ( controller === undefined ) { - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + controller = new WebXRController(); + controllers[ index ] = controller; - // update camera matrices and frustum + } - if ( camera.parent === null ) camera.updateMatrixWorld(); + return controller.getTargetRaySpace(); - if ( xr.enabled === true && xr.isPresenting === true ) { + }; - camera = xr.getCamera( camera ); + this.getControllerGrip = function ( index ) { - } + let controller = controllers[ index ]; - // - if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); + if ( controller === undefined ) { - currentRenderState = renderStates.get( scene, renderStateStack.length ); - currentRenderState.init(); + controller = new WebXRController(); + controllers[ index ] = controller; - renderStateStack.push( currentRenderState ); + } - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromProjectionMatrix( _projScreenMatrix ); + return controller.getGripSpace(); - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); + }; - currentRenderList = renderLists.get( scene, renderListStack.length ); - currentRenderList.init(); + this.getHand = function ( index ) { - renderListStack.push( currentRenderList ); + let controller = controllers[ index ]; - projectObject( scene, camera, 0, _this.sortObjects ); + if ( controller === undefined ) { - currentRenderList.finish(); + controller = new WebXRController(); + controllers[ index ] = controller; - if ( _this.sortObjects === true ) { + } - currentRenderList.sort( _opaqueSort, _transparentSort ); + return controller.getHandSpace(); - } + }; // - if ( _clippingEnabled === true ) clipping.beginShadows(); + function onSessionEvent( event ) { - const shadowsArray = currentRenderState.state.shadowsArray; + const controller = inputSourcesMap.get( event.inputSource ); - shadowMap.render( shadowsArray, scene, camera ); + if ( controller ) { - currentRenderState.setupLights(); - currentRenderState.setupLightsView( camera ); + controller.dispatchEvent( { type: event.type, data: event.inputSource } ); - if ( _clippingEnabled === true ) clipping.endShadows(); + } - // + } - if ( this.info.autoReset === true ) this.info.reset(); + function onSessionEnd() { - if ( renderTarget !== undefined ) { + inputSourcesMap.forEach( function ( controller, inputSource ) { - this.setRenderTarget( renderTarget ); + controller.disconnect( inputSource ); - } + } ); - // + inputSourcesMap.clear(); - background.render( currentRenderList, scene, camera, forceClear ); + _currentDepthNear = null; + _currentDepthFar = null; - // render scene + // restore framebuffer/rendering state - const opaqueObjects = currentRenderList.opaque; - const transparentObjects = currentRenderList.transparent; + renderer.setRenderTarget( initialRenderTarget ); - if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); - if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); + glBaseLayer = null; + glProjLayer = null; + glBinding = null; + session = null; + newRenderTarget = null; - // + // - if ( _currentRenderTarget !== null ) { + animation.stop(); - // Generate mipmap if we're using any kind of mipmap filtering + scope.isPresenting = false; - textures.updateRenderTargetMipmap( _currentRenderTarget ); + scope.dispatchEvent( { type: 'sessionend' } ); - // resolve multisample renderbuffers to a single-sample texture if necessary + } - textures.updateMultisampleRenderTarget( _currentRenderTarget ); + this.setFramebufferScaleFactor = function ( value ) { - } + framebufferScaleFactor = value; - // + if ( scope.isPresenting === true ) { - if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); + console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - // Ensure depth buffer writing is enabled so it can be cleared on next render + } - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); + }; - state.setPolygonOffset( false ); + this.setReferenceSpaceType = function ( value ) { - // _gl.finish(); + referenceSpaceType = value; - bindingStates.resetDefaultState(); - _currentMaterialId = - 1; - _currentCamera = null; + if ( scope.isPresenting === true ) { - renderStateStack.pop(); + console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - if ( renderStateStack.length > 0 ) { + } - currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; + }; - } else { + this.getReferenceSpace = function () { - currentRenderState = null; + return referenceSpace; - } + }; - renderListStack.pop(); + this.getBaseLayer = function () { - if ( renderListStack.length > 0 ) { + return glProjLayer !== null ? glProjLayer : glBaseLayer; - currentRenderList = renderListStack[ renderListStack.length - 1 ]; + }; - } else { + this.getBinding = function () { - currentRenderList = null; + return glBinding; - } + }; - }; + this.getFrame = function () { - function projectObject( object, camera, groupOrder, sortObjects ) { + return xrFrame; - if ( object.visible === false ) return; + }; - const visible = object.layers.test( camera.layers ); + this.getSession = function () { - if ( visible ) { + return session; - if ( object.isGroup ) { + }; - groupOrder = object.renderOrder; + this.setSession = async function ( value ) { - } else if ( object.isLOD ) { + session = value; - if ( object.autoUpdate === true ) object.update( camera ); + if ( session !== null ) { - } else if ( object.isLight ) { + initialRenderTarget = renderer.getRenderTarget(); - currentRenderState.pushLight( object ); + session.addEventListener( 'select', onSessionEvent ); + session.addEventListener( 'selectstart', onSessionEvent ); + session.addEventListener( 'selectend', onSessionEvent ); + session.addEventListener( 'squeeze', onSessionEvent ); + session.addEventListener( 'squeezestart', onSessionEvent ); + session.addEventListener( 'squeezeend', onSessionEvent ); + session.addEventListener( 'end', onSessionEnd ); + session.addEventListener( 'inputsourceschange', onInputSourcesChange ); - if ( object.castShadow ) { + if ( attributes.xrCompatible !== true ) { - currentRenderState.pushShadow( object ); + await gl.makeXRCompatible(); } - } else if ( object.isSprite ) { + if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + const layerInit = { + antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, + alpha: attributes.alpha, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; - if ( sortObjects ) { + glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + session.updateRenderState( { baseLayer: glBaseLayer } ); - } + newRenderTarget = new WebGLRenderTarget( + glBaseLayer.framebufferWidth, + glBaseLayer.framebufferHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + encoding: renderer.outputEncoding + } + ); - const geometry = objects.update( object ); - const material = object.material; + } else { - if ( material.visible ) { + let depthFormat = null; + let depthType = null; + let glDepthFormat = null; - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + if ( attributes.depth ) { + + glDepthFormat = attributes.stencil ? 35056 : 33190; + depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; + depthType = attributes.stencil ? UnsignedInt248Type : UnsignedShortType; } - } + const projectionlayerInit = { + colorFormat: ( renderer.outputEncoding === sRGBEncoding ) ? 35907 : 32856, + depthFormat: glDepthFormat, + scaleFactor: framebufferScaleFactor + }; + + glBinding = new XRWebGLBinding( session, gl ); - } else if ( object.isImmediateRenderObject ) { + glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); - if ( sortObjects ) { + session.updateRenderState( { layers: [ glProjLayer ] } ); + + newRenderTarget = new WebGLRenderTarget( + glProjLayer.textureWidth, + glProjLayer.textureHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), + stencilBuffer: attributes.stencil, + encoding: renderer.outputEncoding, + samples: attributes.antialias ? 4 : 0 + } ); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + const renderTargetProperties = renderer.properties.get( newRenderTarget ); + renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); + newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 - } else if ( object.isMesh || object.isLine || object.isPoints ) { + // Set foveation to maximum. + this.setFoveation( 1.0 ); - if ( object.isSkinnedMesh ) { + referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); - // update skeleton only once in a frame + animation.setContext( session ); + animation.start(); - if ( object.skeleton.frame !== info.render.frame ) { + scope.isPresenting = true; - object.skeleton.update(); - object.skeleton.frame = info.render.frame; + scope.dispatchEvent( { type: 'sessionstart' } ); - } + } - } + }; - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + function onInputSourcesChange( event ) { - if ( sortObjects ) { + const inputSources = session.inputSources; - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + // Assign inputSources to available controllers - } + for ( let i = 0; i < controllers.length; i ++ ) { - const geometry = objects.update( object ); - const material = object.material; - - if ( Array.isArray( material ) ) { - - const groups = geometry.groups; + inputSourcesMap.set( inputSources[ i ], controllers[ i ] ); - for ( let i = 0, l = groups.length; i < l; i ++ ) { + } - const group = groups[ i ]; - const groupMaterial = material[ group.materialIndex ]; + // Notify disconnected - if ( groupMaterial && groupMaterial.visible ) { + for ( let i = 0; i < event.removed.length; i ++ ) { - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + const inputSource = event.removed[ i ]; + const controller = inputSourcesMap.get( inputSource ); - } + if ( controller ) { - } + controller.dispatchEvent( { type: 'disconnected', data: inputSource } ); + inputSourcesMap.delete( inputSource ); - } else if ( material.visible ) { + } - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + } - } + // Notify connected - } + for ( let i = 0; i < event.added.length; i ++ ) { - } + const inputSource = event.added[ i ]; + const controller = inputSourcesMap.get( inputSource ); - } + if ( controller ) { - const children = object.children; + controller.dispatchEvent( { type: 'connected', data: inputSource } ); - for ( let i = 0, l = children.length; i < l; i ++ ) { + } - projectObject( children[ i ], camera, groupOrder, sortObjects ); + } } - } + // - function renderObjects( renderList, scene, camera ) { + const cameraLPos = new Vector3(); + const cameraRPos = new Vector3(); - const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; + /** + * Assumes 2 cameras that are parallel and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 + */ + function setProjectionFromUnion( camera, cameraL, cameraR ) { - for ( let i = 0, l = renderList.length; i < l; i ++ ) { + cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); + cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - const renderItem = renderList[ i ]; + const ipd = cameraLPos.distanceTo( cameraRPos ); - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = overrideMaterial === null ? renderItem.material : overrideMaterial; - const group = renderItem.group; + const projL = cameraL.projectionMatrix.elements; + const projR = cameraR.projectionMatrix.elements; - if ( camera.isArrayCamera ) { + // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // Use the left camera for these values. + const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; + const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - const cameras = camera.cameras; + const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + const left = near * leftFov; + const right = near * rightFov; - for ( let j = 0, jl = cameras.length; j < jl; j ++ ) { + // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + const zOffset = ipd / ( - leftFov + rightFov ); + const xOffset = zOffset * - leftFov; - const camera2 = cameras[ j ]; + // TODO: Better way to apply this offset? + cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); + camera.translateX( xOffset ); + camera.translateZ( zOffset ); + camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - if ( object.layers.test( camera2.layers ) ) { + // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + const near2 = near + zOffset; + const far2 = far + zOffset; + const left2 = left - xOffset; + const right2 = right + ( ipd - xOffset ); + const top2 = topFov * far / far2 * near2; + const bottom2 = bottomFov * far / far2 * near2; - state.viewport( _currentViewport.copy( camera2.viewport ) ); + camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); - currentRenderState.setupLightsView( camera2 ); + } - renderObject( object, scene, camera2, geometry, material, group ); + function updateCamera( camera, parent ) { - } + if ( parent === null ) { - } + camera.matrixWorld.copy( camera.matrix ); } else { - renderObject( object, scene, camera, geometry, material, group ); + camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); } + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); + } - } + this.updateCamera = function ( camera ) { - function renderObject( object, scene, camera, geometry, material, group ) { + if ( session === null ) return; - object.onBeforeRender( _this, scene, camera, geometry, material, group ); + cameraVR.near = cameraR.near = cameraL.near = camera.near; + cameraVR.far = cameraR.far = cameraL.far = camera.far; - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + if ( _currentDepthNear !== cameraVR.near || _currentDepthFar !== cameraVR.far ) { - if ( object.isImmediateRenderObject ) { + // Note that the new renderState won't apply until the next frame. See #18320 - const program = setProgram( camera, scene, material, object ); + session.updateRenderState( { + depthNear: cameraVR.near, + depthFar: cameraVR.far + } ); - state.setMaterial( material ); + _currentDepthNear = cameraVR.near; + _currentDepthFar = cameraVR.far; - bindingStates.reset(); + } - renderObjectImmediate( object, program ); + const parent = camera.parent; + const cameras = cameraVR.cameras; - } else { + updateCamera( cameraVR, parent ); - _this.renderBufferDirect( camera, scene, geometry, material, object, group ); + for ( let i = 0; i < cameras.length; i ++ ) { - } + updateCamera( cameras[ i ], parent ); - object.onAfterRender( _this, scene, camera, geometry, material, group ); + } - } + cameraVR.matrixWorld.decompose( cameraVR.position, cameraVR.quaternion, cameraVR.scale ); - function getProgram( material, scene, object ) { + // update user camera and its children - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + camera.position.copy( cameraVR.position ); + camera.quaternion.copy( cameraVR.quaternion ); + camera.scale.copy( cameraVR.scale ); + camera.matrix.copy( cameraVR.matrix ); + camera.matrixWorld.copy( cameraVR.matrixWorld ); - const materialProperties = properties.get( material ); + const children = camera.children; - const lights = currentRenderState.state.lights; - const shadowsArray = currentRenderState.state.shadowsArray; + for ( let i = 0, l = children.length; i < l; i ++ ) { - const lightsStateVersion = lights.state.version; + children[ i ].updateMatrixWorld( true ); - const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); - const programCacheKey = programCache.getProgramCacheKey( parameters ); + } - let programs = materialProperties.programs; + // update projection matrix for proper view frustum culling - // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change + if ( cameras.length === 2 ) { - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; - materialProperties.fog = scene.fog; - materialProperties.envMap = cubemaps.get( material.envMap || materialProperties.environment ); + setProjectionFromUnion( cameraVR, cameraL, cameraR ); - if ( programs === undefined ) { + } else { - // new material + // assume single camera setup (AR) - material.addEventListener( 'dispose', onMaterialDispose ); + cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); - programs = new Map(); - materialProperties.programs = programs; + } - } + }; - let program = programs.get( programCacheKey ); + this.getCamera = function () { - if ( program !== undefined ) { + return cameraVR; - // early out if program and light state is identical + }; - if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { + this.getFoveation = function () { - updateCommonMaterialProperties( material, parameters ); + if ( glProjLayer !== null ) { - return program; + return glProjLayer.fixedFoveation; } - } else { - - parameters.uniforms = programCache.getUniforms( material ); - - material.onBuild( parameters, _this ); + if ( glBaseLayer !== null ) { - material.onBeforeCompile( parameters, _this ); + return glBaseLayer.fixedFoveation; - program = programCache.acquireProgram( parameters, programCacheKey ); - programs.set( programCacheKey, program ); + } - materialProperties.uniforms = parameters.uniforms; + return undefined; - } + }; - const uniforms = materialProperties.uniforms; + this.setFoveation = function ( foveation ) { - if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { + // 0 = no foveation = full resolution + // 1 = maximum foveation = the edges render at lower resolution - uniforms.clippingPlanes = clipping.uniform; + if ( glProjLayer !== null ) { - } + glProjLayer.fixedFoveation = foveation; - updateCommonMaterialProperties( material, parameters ); + } - // store the light setup it was created for + if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { - materialProperties.needsLights = materialNeedsLights( material ); - materialProperties.lightsStateVersion = lightsStateVersion; + glBaseLayer.fixedFoveation = foveation; - if ( materialProperties.needsLights ) { + } - // wire up the material to this renderer's lighting state + }; - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.directionalLightShadows.value = lights.state.directionalShadow; - uniforms.spotLights.value = lights.state.spot; - uniforms.spotLightShadows.value = lights.state.spotShadow; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.ltc_1.value = lights.state.rectAreaLTC1; - uniforms.ltc_2.value = lights.state.rectAreaLTC2; - uniforms.pointLights.value = lights.state.point; - uniforms.pointLightShadows.value = lights.state.pointShadow; - uniforms.hemisphereLights.value = lights.state.hemi; + // Animation Loop - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms + let onAnimationFrameCallback = null; - } + function onAnimationFrame( time, frame ) { - const progUniforms = program.getUniforms(); - const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); + pose = frame.getViewerPose( referenceSpace ); + xrFrame = frame; - materialProperties.currentProgram = program; - materialProperties.uniformsList = uniformsList; + if ( pose !== null ) { - return program; + const views = pose.views; - } + if ( glBaseLayer !== null ) { - function updateCommonMaterialProperties( material, parameters ) { + renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); + renderer.setRenderTarget( newRenderTarget ); - const materialProperties = properties.get( material ); + } - materialProperties.outputEncoding = parameters.outputEncoding; - materialProperties.instancing = parameters.instancing; - materialProperties.numClippingPlanes = parameters.numClippingPlanes; - materialProperties.numIntersection = parameters.numClipIntersection; - materialProperties.vertexAlphas = parameters.vertexAlphas; + let cameraVRNeedsUpdate = false; - } + // check if it's necessary to rebuild cameraVR's camera list - function setProgram( camera, scene, material, object ) { + if ( views.length !== cameraVR.cameras.length ) { - if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... + cameraVR.cameras.length = 0; + cameraVRNeedsUpdate = true; - textures.resetTextureUnits(); + } - const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; - const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding; - const envMap = cubemaps.get( material.envMap || environment ); - const vertexAlphas = material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4; + for ( let i = 0; i < views.length; i ++ ) { - const materialProperties = properties.get( material ); - const lights = currentRenderState.state.lights; + const view = views[ i ]; - if ( _clippingEnabled === true ) { + let viewport = null; - if ( _localClippingEnabled === true || camera !== _currentCamera ) { + if ( glBaseLayer !== null ) { - const useCache = - camera === _currentCamera && - material.id === _currentMaterialId; + viewport = glBaseLayer.getViewport( view ); - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - clipping.setState( material, camera, useCache ); + } else { - } + const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); + viewport = glSubImage.viewport; - } + // For side-by-side projection, we only produce a single texture for both eyes. + if ( i === 0 ) { - // + renderer.setRenderTargetTextures( + newRenderTarget, + glSubImage.colorTexture, + glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); - let needsProgramChange = false; + renderer.setRenderTarget( newRenderTarget ); - if ( material.version === materialProperties.__version ) { + } - if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { + } - needsProgramChange = true; + const camera = cameras[ i ]; - } else if ( materialProperties.outputEncoding !== encoding ) { + camera.matrix.fromArray( view.transform.matrix ); + camera.projectionMatrix.fromArray( view.projectionMatrix ); + camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - needsProgramChange = true; + if ( i === 0 ) { - } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { + cameraVR.matrix.copy( camera.matrix ); - needsProgramChange = true; + } - } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { + if ( cameraVRNeedsUpdate === true ) { - needsProgramChange = true; + cameraVR.cameras.push( camera ); - } else if ( materialProperties.envMap !== envMap ) { + } - needsProgramChange = true; + } - } else if ( material.fog && materialProperties.fog !== fog ) { + } - needsProgramChange = true; + // - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== clipping.numPlanes || - materialProperties.numIntersection !== clipping.numIntersection ) ) { + const inputSources = session.inputSources; - needsProgramChange = true; + for ( let i = 0; i < controllers.length; i ++ ) { - } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { + const controller = controllers[ i ]; + const inputSource = inputSources[ i ]; - needsProgramChange = true; + controller.update( inputSource, frame, referenceSpace ); } - } else { + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - needsProgramChange = true; - materialProperties.__version = material.version; + xrFrame = null; } - // + const animation = new WebGLAnimation(); - let program = materialProperties.currentProgram; + animation.setAnimationLoop( onAnimationFrame ); - if ( needsProgramChange === true ) { + this.setAnimationLoop = function ( callback ) { - program = getProgram( material, scene, object ); + onAnimationFrameCallback = callback; - } + }; - let refreshProgram = false; - let refreshMaterial = false; - let refreshLights = false; + this.dispose = function () {}; - const p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.uniforms; + } - if ( state.useProgram( program.program ) ) { +} - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; +function WebGLMaterials( renderer, properties ) { - } + function refreshFogUniforms( uniforms, fog ) { - if ( material.id !== _currentMaterialId ) { + uniforms.fogColor.value.copy( fog.color ); - _currentMaterialId = material.id; + if ( fog.isFog ) { - refreshMaterial = true; + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - } + } else if ( fog.isFogExp2 ) { - if ( refreshProgram || _currentCamera !== camera ) { + uniforms.fogDensity.value = fog.density; - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + } - if ( capabilities.logarithmicDepthBuffer ) { + } - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { - } + if ( material.isMeshBasicMaterial ) { - if ( _currentCamera !== camera ) { + refreshUniformsCommon( uniforms, material ); - _currentCamera = camera; + } else if ( material.isMeshLambertMaterial ) { - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: + refreshUniformsCommon( uniforms, material ); - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done + } else if ( material.isMeshToonMaterial ) { - } + refreshUniformsCommon( uniforms, material ); + refreshUniformsToon( uniforms, material ); - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + } else if ( material.isMeshPhongMaterial ) { - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshStandardMaterial || - material.envMap ) { + refreshUniformsCommon( uniforms, material ); + refreshUniformsPhong( uniforms, material ); - const uCamPos = p_uniforms.map.cameraPosition; + } else if ( material.isMeshStandardMaterial ) { - if ( uCamPos !== undefined ) { + refreshUniformsCommon( uniforms, material ); + refreshUniformsStandard( uniforms, material ); - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); + if ( material.isMeshPhysicalMaterial ) { - } + refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); } - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial ) { + } else if ( material.isMeshMatcapMaterial ) { - p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsMatcap( uniforms, material ); - } + } else if ( material.isMeshDepthMaterial ) { - if ( material.isMeshPhongMaterial || - material.isMeshToonMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.isShadowMaterial || - material.skinning ) { + refreshUniformsCommon( uniforms, material ); - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + } else if ( material.isMeshDistanceMaterial ) { - } + refreshUniformsCommon( uniforms, material ); + refreshUniformsDistance( uniforms, material ); - } + } else if ( material.isMeshNormalMaterial ) { - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // otherwise textures used for skinning can take over texture units reserved for other material textures + refreshUniformsCommon( uniforms, material ); - if ( material.skinning ) { + } else if ( material.isLineBasicMaterial ) { - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); + refreshUniformsLine( uniforms, material ); - const skeleton = object.skeleton; + if ( material.isLineDashedMaterial ) { - if ( skeleton ) { + refreshUniformsDash( uniforms, material ); - const bones = skeleton.bones; + } - if ( capabilities.floatVertexTextures ) { + } else if ( material.isPointsMaterial ) { - if ( skeleton.boneTexture === null ) { + refreshUniformsPoints( uniforms, material, pixelRatio, height ); - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + } else if ( material.isSpriteMaterial ) { + refreshUniformsSprites( uniforms, material ); - let size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); + } else if ( material.isShadowMaterial ) { - const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values + uniforms.color.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + } else if ( material.isShaderMaterial ) { - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; + material.uniformsNeedUpdate = false; // #15581 - } + } - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); + } - } else { + function refreshUniformsCommon( uniforms, material ) { - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); + uniforms.opacity.value = material.opacity; - } + if ( material.color ) { - } + uniforms.diffuse.value.copy( material.color ); } - if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { + if ( material.emissive ) { - materialProperties.receiveShadow = object.receiveShadow; - p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } - if ( refreshMaterial ) { + if ( material.map ) { - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); + uniforms.map.value = material.map; - if ( materialProperties.needsLights ) { + } - // the current material requires lighting info + if ( material.alphaMap ) { - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required + uniforms.alphaMap.value = material.alphaMap; - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + } - } + if ( material.bumpMap ) { - // refresh uniforms common to several materials + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; - if ( fog && material.fog ) { + } - materials.refreshFogUniforms( m_uniforms, fog ); + if ( material.displacementMap ) { - } + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height ); + } - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + if ( material.emissiveMap ) { + + uniforms.emissiveMap.value = material.emissiveMap; } - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { + if ( material.normalMap ) { - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } - if ( material.isSpriteMaterial ) { + if ( material.specularMap ) { - p_uniforms.setValue( _gl, 'center', object.center ); + uniforms.specularMap.value = material.specularMap; } - // common matrices + if ( material.alphaTest > 0 ) { - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); + uniforms.alphaTest.value = material.alphaTest; - return program; + } - } + const envMap = properties.get( material ).envMap; - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + if ( envMap ) { - function markUniformsLightsNeedsUpdate( uniforms, value ) { + uniforms.envMap.value = envMap; - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; + uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - uniforms.directionalLights.needsUpdate = value; - uniforms.directionalLightShadows.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.pointLightShadows.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.spotLightShadows.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; - - } - - function materialNeedsLights( material ) { - - return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || - material.isMeshStandardMaterial || material.isShadowMaterial || - ( material.isShaderMaterial && material.lights === true ); - - } + uniforms.reflectivity.value = material.reflectivity; + uniforms.ior.value = material.ior; + uniforms.refractionRatio.value = material.refractionRatio; - this.getActiveCubeFace = function () { + } - return _currentActiveCubeFace; + if ( material.lightMap ) { - }; + uniforms.lightMap.value = material.lightMap; - this.getActiveMipmapLevel = function () { + // artist-friendly light intensity scaling factor + const scaleFactor = ( renderer.physicallyCorrectLights !== true ) ? Math.PI : 1; - return _currentActiveMipmapLevel; + uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; - }; + } - this.getRenderTarget = function () { + if ( material.aoMap ) { - return _currentRenderTarget; + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - }; + } - this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. displacementMap map + // 4. normal map + // 5. bump map + // 6. roughnessMap map + // 7. metalnessMap map + // 8. alphaMap map + // 9. emissiveMap map + // 10. clearcoat map + // 11. clearcoat normal map + // 12. clearcoat roughnessMap map + // 13. specular intensity map + // 14. specular tint map + // 15. transmission map + // 16. thickness map - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; + let uvScaleMap; - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + if ( material.map ) { - textures.setupRenderTarget( renderTarget ); + uvScaleMap = material.map; - } + } else if ( material.specularMap ) { - let framebuffer = null; - let isCube = false; - let isRenderTarget3D = false; + uvScaleMap = material.specularMap; - if ( renderTarget ) { + } else if ( material.displacementMap ) { - const texture = renderTarget.texture; + uvScaleMap = material.displacementMap; - if ( texture.isDataTexture3D || texture.isDataTexture2DArray ) { + } else if ( material.normalMap ) { - isRenderTarget3D = true; + uvScaleMap = material.normalMap; - } + } else if ( material.bumpMap ) { - const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; + uvScaleMap = material.bumpMap; - if ( renderTarget.isWebGLCubeRenderTarget ) { + } else if ( material.roughnessMap ) { - framebuffer = __webglFramebuffer[ activeCubeFace ]; - isCube = true; + uvScaleMap = material.roughnessMap; - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + } else if ( material.metalnessMap ) { - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + uvScaleMap = material.metalnessMap; - } else { + } else if ( material.alphaMap ) { - framebuffer = __webglFramebuffer; + uvScaleMap = material.alphaMap; - } + } else if ( material.emissiveMap ) { - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; + uvScaleMap = material.emissiveMap; - } else { + } else if ( material.clearcoatMap ) { - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; + uvScaleMap = material.clearcoatMap; - } + } else if ( material.clearcoatNormalMap ) { - state.bindFramebuffer( 36160, framebuffer ); + uvScaleMap = material.clearcoatNormalMap; - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); + } else if ( material.clearcoatRoughnessMap ) { - if ( isCube ) { + uvScaleMap = material.clearcoatRoughnessMap; - const textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); + } else if ( material.specularIntensityMap ) { - } else if ( isRenderTarget3D ) { + uvScaleMap = material.specularIntensityMap; - const textureProperties = properties.get( renderTarget.texture ); - const layer = activeCubeFace || 0; - _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); + } else if ( material.specularColorMap ) { - } + uvScaleMap = material.specularColorMap; - }; + } else if ( material.transmissionMap ) { - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + uvScaleMap = material.transmissionMap; - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + } else if ( material.thicknessMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + uvScaleMap = material.thicknessMap; - } + } else if ( material.sheenColorMap ) { - let framebuffer = properties.get( renderTarget ).__webglFramebuffer; + uvScaleMap = material.sheenColorMap; - if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { + } else if ( material.sheenRoughnessMap ) { - framebuffer = framebuffer[ activeCubeFaceIndex ]; + uvScaleMap = material.sheenRoughnessMap; } - if ( framebuffer ) { - - state.bindFramebuffer( 36160, framebuffer ); - - try { - - const texture = renderTarget.texture; - const textureFormat = texture.format; - const textureType = texture.type; + if ( uvScaleMap !== undefined ) { - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { + // backwards compatibility + if ( uvScaleMap.isWebGLRenderTarget ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; + uvScaleMap = uvScaleMap.texture; - } + } - const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); + if ( uvScaleMap.matrixAutoUpdate === true ) { - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! halfFloatSupportedByExt ) { + uvScaleMap.updateMatrix(); - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; + } - } + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { + } - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + // uv repeat and offset setting priorities for uv2 + // 1. ao map + // 2. light map - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + let uv2ScaleMap; - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + if ( material.aoMap ) { - } + uv2ScaleMap = material.aoMap; - } else { + } else if ( material.lightMap ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + uv2ScaleMap = material.lightMap; - } + } - } finally { + if ( uv2ScaleMap !== undefined ) { - // restore framebuffer of current render target if necessary + // backwards compatibility + if ( uv2ScaleMap.isWebGLRenderTarget ) { - const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; - state.bindFramebuffer( 36160, framebuffer ); + uv2ScaleMap = uv2ScaleMap.texture; } - } - - }; - - this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - - const levelScale = Math.pow( 2, - level ); - const width = Math.floor( texture.image.width * levelScale ); - const height = Math.floor( texture.image.height * levelScale ); - const glFormat = utils.convert( texture.format ); - - textures.setTexture2D( texture, 0 ); + if ( uv2ScaleMap.matrixAutoUpdate === true ) { - _gl.copyTexImage2D( 3553, level, glFormat, position.x, position.y, width, height, 0 ); + uv2ScaleMap.updateMatrix(); - state.unbindTexture(); + } - }; + uniforms.uv2Transform.value.copy( uv2ScaleMap.matrix ); - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { + } - const width = srcTexture.image.width; - const height = srcTexture.image.height; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); + } - textures.setTexture2D( dstTexture, 0 ); + function refreshUniformsLine( uniforms, material ) { - // As another texture upload may have changed pixelStorei - // parameters, make sure they are correct for the dstTexture - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - if ( srcTexture.isDataTexture ) { + } - _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); + function refreshUniformsDash( uniforms, material ) { - } else { + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; - if ( srcTexture.isCompressedTexture ) { + } - _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); + function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - } else { + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * pixelRatio; + uniforms.scale.value = height * 0.5; - _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); + if ( material.map ) { - } + uniforms.map.value = material.map; } - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - - state.unbindTexture(); + if ( material.alphaMap ) { - }; + uniforms.alphaMap.value = material.alphaMap; - this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { + } - if ( _this.isWebGL1Renderer ) { + if ( material.alphaTest > 0 ) { - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); - return; + uniforms.alphaTest.value = material.alphaTest; } - const { width, height, data } = srcTexture.image; - const glFormat = utils.convert( dstTexture.format ); - const glType = utils.convert( dstTexture.type ); - let glTarget; - - if ( dstTexture.isDataTexture3D ) { + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map - textures.setTexture3D( dstTexture, 0 ); - glTarget = 32879; + let uvScaleMap; - } else if ( dstTexture.isDataTexture2DArray ) { + if ( material.map ) { - textures.setTexture2DArray( dstTexture, 0 ); - glTarget = 35866; + uvScaleMap = material.map; - } else { + } else if ( material.alphaMap ) { - console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); - return; + uvScaleMap = material.alphaMap; } - _gl.pixelStorei( 37440, dstTexture.flipY ); - _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); - _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - - const unpackRowLen = _gl.getParameter( 3314 ); - const unpackImageHeight = _gl.getParameter( 32878 ); - const unpackSkipPixels = _gl.getParameter( 3316 ); - const unpackSkipRows = _gl.getParameter( 3315 ); - const unpackSkipImages = _gl.getParameter( 32877 ); + if ( uvScaleMap !== undefined ) { - _gl.pixelStorei( 3314, width ); - _gl.pixelStorei( 32878, height ); - _gl.pixelStorei( 3316, sourceBox.min.x ); - _gl.pixelStorei( 3315, sourceBox.min.y ); - _gl.pixelStorei( 32877, sourceBox.min.z ); + if ( uvScaleMap.matrixAutoUpdate === true ) { - _gl.texSubImage3D( - glTarget, - level, - position.x, - position.y, - position.z, - sourceBox.max.x - sourceBox.min.x + 1, - sourceBox.max.y - sourceBox.min.y + 1, - sourceBox.max.z - sourceBox.min.z + 1, - glFormat, - glType, - data - ); + uvScaleMap.updateMatrix(); - _gl.pixelStorei( 3314, unpackRowLen ); - _gl.pixelStorei( 32878, unpackImageHeight ); - _gl.pixelStorei( 3316, unpackSkipPixels ); - _gl.pixelStorei( 3315, unpackSkipRows ); - _gl.pixelStorei( 32877, unpackSkipImages ); + } - // Generate mipmaps only when copying level 0 - if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - state.unbindTexture(); + } - }; + } - this.initTexture = function ( texture ) { + function refreshUniformsSprites( uniforms, material ) { - textures.setTexture2D( texture, 0 ); + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; - state.unbindTexture(); + if ( material.map ) { - }; + uniforms.map.value = material.map; - this.resetState = function () { + } - _currentActiveCubeFace = 0; - _currentActiveMipmapLevel = 0; - _currentRenderTarget = null; + if ( material.alphaMap ) { - state.reset(); - bindingStates.reset(); + uniforms.alphaMap.value = material.alphaMap; - }; + } - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + if ( material.alphaTest > 0 ) { - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + uniforms.alphaTest.value = material.alphaTest; - } + } -} + // uv repeat and offset setting priorities + // 1. color map + // 2. alpha map -class WebGL1Renderer extends WebGLRenderer {} + let uvScaleMap; -WebGL1Renderer.prototype.isWebGL1Renderer = true; + if ( material.map ) { -class Scene extends Object3D { + uvScaleMap = material.map; - constructor() { + } else if ( material.alphaMap ) { - super(); + uvScaleMap = material.alphaMap; - this.type = 'Scene'; + } - this.background = null; - this.environment = null; - this.fog = null; + if ( uvScaleMap !== undefined ) { - this.overrideMaterial = null; + if ( uvScaleMap.matrixAutoUpdate === true ) { - this.autoUpdate = true; // checked by the renderer + uvScaleMap.updateMatrix(); - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + } - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + uniforms.uvTransform.value.copy( uvScaleMap.matrix ); } } - copy( source, recursive ) { - - super.copy( source, recursive ); - - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.environment !== null ) this.environment = source.environment.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; + function refreshUniformsPhong( uniforms, material ) { - return this; + uniforms.specular.value.copy( material.specular ); + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) } - toJSON( meta ) { + function refreshUniformsToon( uniforms, material ) { - const data = super.toJSON( meta ); + if ( material.gradientMap ) { - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.environment !== null ) data.object.environment = this.environment.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); + uniforms.gradientMap.value = material.gradientMap; - return data; + } } -} + function refreshUniformsStandard( uniforms, material ) { -Scene.prototype.isScene = true; + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; -class InterleavedBuffer { + if ( material.roughnessMap ) { - constructor( array, stride ) { + uniforms.roughnessMap.value = material.roughnessMap; - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; + } - this.usage = StaticDrawUsage; - this.updateRange = { offset: 0, count: - 1 }; + if ( material.metalnessMap ) { - this.version = 0; + uniforms.metalnessMap.value = material.metalnessMap; - this.uuid = generateUUID(); + } - this.onUploadCallback = function () {}; + const envMap = properties.get( material ).envMap; - } + if ( envMap ) { - set needsUpdate( value ) { + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; - if ( value === true ) this.version ++; + } } - setUsage( value ) { - - this.usage = value; + function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { - return this; + uniforms.ior.value = material.ior; // also part of uniforms common - } + if ( material.sheen > 0 ) { - copy( source ) { + uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.usage = source.usage; + uniforms.sheenRoughness.value = material.sheenRoughness; - return this; + if ( material.sheenColorMap ) { - } + uniforms.sheenColorMap.value = material.sheenColorMap; - copyAt( index1, attribute, index2 ) { + } - index1 *= this.stride; - index2 *= attribute.stride; + if ( material.sheenRoughnessMap ) { - for ( let i = 0, l = this.stride; i < l; i ++ ) { + uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + } } - return this; - - } + if ( material.clearcoat > 0 ) { - set( value, offset = 0 ) { + uniforms.clearcoat.value = material.clearcoat; + uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - this.array.set( value, offset ); + if ( material.clearcoatMap ) { - return this; + uniforms.clearcoatMap.value = material.clearcoatMap; - } + } - clone( data ) { + if ( material.clearcoatRoughnessMap ) { - if ( data.arrayBuffers === undefined ) { + uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - data.arrayBuffers = {}; + } - } + if ( material.clearcoatNormalMap ) { - if ( this.array.buffer._uuid === undefined ) { + uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); + uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - this.array.buffer._uuid = generateUUID(); + if ( material.side === BackSide ) { - } + uniforms.clearcoatNormalScale.value.negate(); - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + } - data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; + } } - const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - - const ib = new InterleavedBuffer( array, this.stride ); - ib.setUsage( this.usage ); + if ( material.transmission > 0 ) { - return ib; + uniforms.transmission.value = material.transmission; + uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; + uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); - } + if ( material.transmissionMap ) { - onUpload( callback ) { + uniforms.transmissionMap.value = material.transmissionMap; - this.onUploadCallback = callback; + } - return this; + uniforms.thickness.value = material.thickness; - } + if ( material.thicknessMap ) { - toJSON( data ) { + uniforms.thicknessMap.value = material.thicknessMap; - if ( data.arrayBuffers === undefined ) { + } - data.arrayBuffers = {}; + uniforms.attenuationDistance.value = material.attenuationDistance; + uniforms.attenuationColor.value.copy( material.attenuationColor ); } - // generate UUID for array buffer if necessary + uniforms.specularIntensity.value = material.specularIntensity; + uniforms.specularColor.value.copy( material.specularColor ); - if ( this.array.buffer._uuid === undefined ) { + if ( material.specularIntensityMap ) { - this.array.buffer._uuid = generateUUID(); + uniforms.specularIntensityMap.value = material.specularIntensityMap; } - if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { + if ( material.specularColorMap ) { - data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); + uniforms.specularColorMap.value = material.specularColorMap; } - // - - return { - uuid: this.uuid, - buffer: this.array.buffer._uuid, - type: this.array.constructor.name, - stride: this.stride - }; - } -} - -InterleavedBuffer.prototype.isInterleavedBuffer = true; - -const _vector$6 = new Vector3(); - -class InterleavedBufferAttribute { - - constructor( interleavedBuffer, itemSize, offset, normalized ) { + function refreshUniformsMatcap( uniforms, material ) { - this.name = ''; + if ( material.matcap ) { - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; + uniforms.matcap.value = material.matcap; - this.normalized = normalized === true; + } } - get count() { + function refreshUniformsDistance( uniforms, material ) { - return this.data.count; + uniforms.referencePosition.value.copy( material.referencePosition ); + uniforms.nearDistance.value = material.nearDistance; + uniforms.farDistance.value = material.farDistance; } - get array() { + return { + refreshFogUniforms: refreshFogUniforms, + refreshMaterialUniforms: refreshMaterialUniforms + }; - return this.data.array; +} - } +function createCanvasElement() { - set needsUpdate( value ) { + const canvas = createElementNS( 'canvas' ); + canvas.style.display = 'block'; + return canvas; - this.data.needsUpdate = value; +} - } +function WebGLRenderer( parameters = {} ) { - applyMatrix4( m ) { + const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(), + _context = parameters.context !== undefined ? parameters.context : null, - for ( let i = 0, l = this.data.count; i < l; i ++ ) { + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', + _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + let _alpha; - _vector$6.applyMatrix4( m ); + if ( parameters.context !== undefined ) { - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + _alpha = _context.getContextAttributes().alpha; - } + } else { - return this; + _alpha = parameters.alpha !== undefined ? parameters.alpha : false; } - applyNormalMatrix( m ) { - - for ( let i = 0, l = this.count; i < l; i ++ ) { + let currentRenderList = null; + let currentRenderState = null; - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. - _vector$6.applyNormalMatrix( m ); + const renderListStack = []; + const renderStateStack = []; - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + // public properties - } + this.domElement = _canvas; - return this; + // Debug configuration container + this.debug = { - } + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true + }; - transformDirection( m ) { + // clearing - for ( let i = 0, l = this.count; i < l; i ++ ) { + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - _vector$6.x = this.getX( i ); - _vector$6.y = this.getY( i ); - _vector$6.z = this.getZ( i ); + // scene graph - _vector$6.transformDirection( m ); + this.sortObjects = true; - this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); + // user-defined clipping - } + this.clippingPlanes = []; + this.localClippingEnabled = false; - return this; + // physically based shading - } + this.outputEncoding = LinearEncoding; - setX( index, x ) { + // physical lights - this.data.array[ index * this.data.stride + this.offset ] = x; + this.physicallyCorrectLights = false; - return this; + // tone mapping - } + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; - setY( index, y ) { + // internal properties - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + const _this = this; - return this; + let _isContextLost = false; - } + // internal state cache - setZ( index, z ) { + let _currentActiveCubeFace = 0; + let _currentActiveMipmapLevel = 0; + let _currentRenderTarget = null; + let _currentMaterialId = - 1; - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + let _currentCamera = null; - return this; + const _currentViewport = new Vector4(); + const _currentScissor = new Vector4(); + let _currentScissorTest = null; - } + // - setW( index, w ) { + let _width = _canvas.width; + let _height = _canvas.height; - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + let _pixelRatio = 1; + let _opaqueSort = null; + let _transparentSort = null; - return this; + const _viewport = new Vector4( 0, 0, _width, _height ); + const _scissor = new Vector4( 0, 0, _width, _height ); + let _scissorTest = false; - } + // frustum - getX( index ) { + const _frustum = new Frustum(); - return this.data.array[ index * this.data.stride + this.offset ]; + // clipping - } + let _clippingEnabled = false; + let _localClippingEnabled = false; - getY( index ) { + // transmission - return this.data.array[ index * this.data.stride + this.offset + 1 ]; + let _transmissionRenderTarget = null; - } + // camera matrices cache - getZ( index ) { + const _projScreenMatrix = new Matrix4(); - return this.data.array[ index * this.data.stride + this.offset + 2 ]; + const _vector2 = new Vector2(); + const _vector3 = new Vector3(); - } + const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; - getW( index ) { + function getTargetPixelRatio() { - return this.data.array[ index * this.data.stride + this.offset + 3 ]; + return _currentRenderTarget === null ? _pixelRatio : 1; } - setXY( index, x, y ) { + // initialize - index = index * this.data.stride + this.offset; + let _gl = _context; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; + function getContext( contextNames, contextAttributes ) { - return this; + for ( let i = 0; i < contextNames.length; i ++ ) { - } + const contextName = contextNames[ i ]; + const context = _canvas.getContext( contextName, contextAttributes ); + if ( context !== null ) return context; - setXYZ( index, x, y, z ) { + } - index = index * this.data.stride + this.offset; + return null; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; + } - return this; + try { - } + const contextAttributes = { + alpha: true, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer, + powerPreference: _powerPreference, + failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat + }; - setXYZW( index, x, y, z, w ) { + // OffscreenCanvas does not have setAttribute, see #22811 + if ( 'setAttribute' in _canvas ) _canvas.setAttribute( 'data-engine', `three.js r${REVISION}` ); - index = index * this.data.stride + this.offset; + // event listeners must be registered before WebGL context is created, see #12753 + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; + if ( _gl === null ) { - return this; + const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - } + if ( _this.isWebGL1Renderer === true ) { - clone( data ) { + contextNames.shift(); - if ( data === undefined ) { + } - console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); + _gl = getContext( contextNames, contextAttributes ); - const array = []; + if ( _gl === null ) { - for ( let i = 0; i < this.count; i ++ ) { + if ( getContext( contextNames ) ) { - const index = i * this.data.stride + this.offset; + throw new Error( 'Error creating WebGL context with your selected attributes.' ); - for ( let j = 0; j < this.itemSize; j ++ ) { + } else { - array.push( this.data.array[ index + j ] ); + throw new Error( 'Error creating WebGL context.' ); } } - return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); - - } else { + } - if ( data.interleavedBuffers === undefined ) { + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - data.interleavedBuffers = {}; + if ( _gl.getShaderPrecisionFormat === undefined ) { - } + _gl.getShaderPrecisionFormat = function () { - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); + }; - } + } - return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); + } catch ( error ) { - } + console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; } - toJSON( data ) { + let extensions, capabilities, state, info; + let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let programCache, materials, renderLists, renderStates, clipping, shadowMap; - if ( data === undefined ) { + let background, morphtargets, bufferRenderer, indexedBufferRenderer; - console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); + let utils, bindingStates; - const array = []; + function initGLContext() { - for ( let i = 0; i < this.count; i ++ ) { + extensions = new WebGLExtensions( _gl ); - const index = i * this.data.stride + this.offset; + capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - for ( let j = 0; j < this.itemSize; j ++ ) { + extensions.init( capabilities ); - array.push( this.data.array[ index + j ] ); + utils = new WebGLUtils( _gl, extensions, capabilities ); - } + state = new WebGLState( _gl, extensions, capabilities ); - } + info = new WebGLInfo( _gl ); + properties = new WebGLProperties(); + textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); + cubemaps = new WebGLCubeMaps( _this ); + cubeuvmaps = new WebGLCubeUVMaps( _this ); + attributes = new WebGLAttributes( _gl, capabilities ); + bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); + geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); + morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); + clipping = new WebGLClipping( properties ); + programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + materials = new WebGLMaterials( _this, properties ); + renderLists = new WebGLRenderLists(); + renderStates = new WebGLRenderStates( extensions, capabilities ); + background = new WebGLBackground( _this, cubemaps, state, objects, _alpha, _premultipliedAlpha ); + shadowMap = new WebGLShadowMap( _this, objects, capabilities ); - // deinterleave data and save it as an ordinary buffer attribute for now + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: array, - normalized: this.normalized - }; + info.programs = programCache.programs; - } else { + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.shadowMap = shadowMap; + _this.state = state; + _this.info = info; - // save as true interlaved attribtue + } - if ( data.interleavedBuffers === undefined ) { + initGLContext(); - data.interleavedBuffers = {}; + // xr - } + const xr = new WebXRManager( _this, _gl ); - if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + this.xr = xr; - data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); + // API - } + this.getContext = function () { - return { - isInterleavedBufferAttribute: true, - itemSize: this.itemSize, - data: this.data.uuid, - offset: this.offset, - normalized: this.normalized - }; + return _gl; - } + }; - } + this.getContextAttributes = function () { -} + return _gl.getContextAttributes(); -InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; + }; -/** - * parameters = { - * color: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ + this.forceContextLoss = function () { -class SpriteMaterial extends Material { + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.loseContext(); - constructor( parameters ) { + }; - super(); + this.forceContextRestore = function () { - this.type = 'SpriteMaterial'; + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.restoreContext(); - this.color = new Color( 0xffffff ); + }; - this.map = null; + this.getPixelRatio = function () { - this.alphaMap = null; + return _pixelRatio; - this.rotation = 0; + }; - this.sizeAttenuation = true; + this.setPixelRatio = function ( value ) { - this.transparent = true; + if ( value === undefined ) return; - this.setValues( parameters ); + _pixelRatio = value; - } + this.setSize( _width, _height, false ); - copy( source ) { + }; - super.copy( source ); + this.getSize = function ( target ) { - this.color.copy( source.color ); + return target.set( _width, _height ); - this.map = source.map; + }; - this.alphaMap = source.alphaMap; + this.setSize = function ( width, height, updateStyle ) { - this.rotation = source.rotation; + if ( xr.isPresenting ) { - this.sizeAttenuation = source.sizeAttenuation; + console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + return; - return this; + } - } + _width = width; + _height = height; -} + _canvas.width = Math.floor( width * _pixelRatio ); + _canvas.height = Math.floor( height * _pixelRatio ); -SpriteMaterial.prototype.isSpriteMaterial = true; + if ( updateStyle !== false ) { -let _geometry; + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; -const _intersectPoint = /*@__PURE__*/ new Vector3(); -const _worldScale = /*@__PURE__*/ new Vector3(); -const _mvPosition = /*@__PURE__*/ new Vector3(); + } -const _alignedPosition = /*@__PURE__*/ new Vector2(); -const _rotatedPosition = /*@__PURE__*/ new Vector2(); -const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); + this.setViewport( 0, 0, width, height ); -const _vA = /*@__PURE__*/ new Vector3(); -const _vB = /*@__PURE__*/ new Vector3(); -const _vC = /*@__PURE__*/ new Vector3(); + }; -const _uvA = /*@__PURE__*/ new Vector2(); -const _uvB = /*@__PURE__*/ new Vector2(); -const _uvC = /*@__PURE__*/ new Vector2(); + this.getDrawingBufferSize = function ( target ) { -class Sprite extends Object3D { + return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - constructor( material ) { + }; - super(); + this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - this.type = 'Sprite'; + _width = width; + _height = height; - if ( _geometry === undefined ) { + _pixelRatio = pixelRatio; - _geometry = new BufferGeometry(); + _canvas.width = Math.floor( width * pixelRatio ); + _canvas.height = Math.floor( height * pixelRatio ); - const float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); + this.setViewport( 0, 0, width, height ); - const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); + }; - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); + this.getCurrentViewport = function ( target ) { - } + return target.copy( _currentViewport ); - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); + }; - this.center = new Vector2( 0.5, 0.5 ); + this.getViewport = function ( target ) { - } + return target.copy( _viewport ); - raycast( raycaster, intersects ) { + }; - if ( raycaster.camera === null ) { + this.setViewport = function ( x, y, width, height ) { - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); + if ( x.isVector4 ) { - } + _viewport.set( x.x, x.y, x.z, x.w ); - _worldScale.setFromMatrixScale( this.matrixWorld ); + } else { - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); + _viewport.set( x, y, width, height ); - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + } - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - _worldScale.multiplyScalar( - _mvPosition.z ); + }; - } + this.getScissor = function ( target ) { - const rotation = this.material.rotation; - let sin, cos; + return target.copy( _scissor ); - if ( rotation !== 0 ) { + }; - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); + this.setScissor = function ( x, y, width, height ) { - } + if ( x.isVector4 ) { - const center = this.center; + _scissor.set( x.x, x.y, x.z, x.w ); - transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + } else { - _uvA.set( 0, 0 ); - _uvB.set( 1, 0 ); - _uvC.set( 1, 1 ); + _scissor.set( x, y, width, height ); - // check first triangle - let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); + } - if ( intersect === null ) { + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - // check second triangle - transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB.set( 0, 1 ); + }; - intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); - if ( intersect === null ) { + this.getScissorTest = function () { - return; + return _scissorTest; - } + }; - } + this.setScissorTest = function ( boolean ) { - const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); + state.setScissorTest( _scissorTest = boolean ); - if ( distance < raycaster.near || distance > raycaster.far ) return; + }; - intersects.push( { + this.setOpaqueSort = function ( method ) { - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), - face: null, - object: this + _opaqueSort = method; - } ); + }; - } + this.setTransparentSort = function ( method ) { - copy( source ) { + _transparentSort = method; - super.copy( source ); + }; - if ( source.center !== undefined ) this.center.copy( source.center ); + // Clearing - this.material = source.material; + this.getClearColor = function ( target ) { - return this; + return target.copy( background.getClearColor() ); - } + }; -} + this.setClearColor = function () { -Sprite.prototype.isSprite = true; + background.setClearColor.apply( background, arguments ); -function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { + }; - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); + this.getClearAlpha = function () { - // to check if rotation is not zero - if ( sin !== undefined ) { + return background.getClearAlpha(); - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); + }; - } else { + this.setClearAlpha = function () { - _rotatedPosition.copy( _alignedPosition ); + background.setClearAlpha.apply( background, arguments ); - } + }; + this.clear = function ( color = true, depth = true, stencil = true ) { - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; + let bits = 0; - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); + if ( color ) bits |= 16384; + if ( depth ) bits |= 256; + if ( stencil ) bits |= 1024; -} + _gl.clear( bits ); -const _basePosition = /*@__PURE__*/ new Vector3(); + }; -const _skinIndex = /*@__PURE__*/ new Vector4(); -const _skinWeight = /*@__PURE__*/ new Vector4(); + this.clearColor = function () { -const _vector$5 = /*@__PURE__*/ new Vector3(); -const _matrix = /*@__PURE__*/ new Matrix4(); + this.clear( true, false, false ); -class SkinnedMesh extends Mesh { + }; - constructor( geometry, material ) { + this.clearDepth = function () { - super( geometry, material ); + this.clear( false, true, false ); - this.type = 'SkinnedMesh'; + }; - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); + this.clearStencil = function () { - } + this.clear( false, false, true ); - copy( source ) { + }; - super.copy( source ); + // - this.bindMode = source.bindMode; - this.bindMatrix.copy( source.bindMatrix ); - this.bindMatrixInverse.copy( source.bindMatrixInverse ); + this.dispose = function () { - this.skeleton = source.skeleton; + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); - return this; + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + cubeuvmaps.dispose(); + objects.dispose(); + bindingStates.dispose(); + programCache.dispose(); - } + xr.dispose(); - bind( skeleton, bindMatrix ) { + xr.removeEventListener( 'sessionstart', onXRSessionStart ); + xr.removeEventListener( 'sessionend', onXRSessionEnd ); - this.skeleton = skeleton; + if ( _transmissionRenderTarget ) { - if ( bindMatrix === undefined ) { + _transmissionRenderTarget.dispose(); + _transmissionRenderTarget = null; - this.updateMatrixWorld( true ); + } - this.skeleton.calculateInverses(); + animation.stop(); - bindMatrix = this.matrixWorld; + }; - } + // Events - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.copy( bindMatrix ).invert(); + function onContextLost( event ) { - } + event.preventDefault(); - pose() { + console.log( 'THREE.WebGLRenderer: Context Lost.' ); - this.skeleton.pose(); + _isContextLost = true; } - normalizeSkinWeights() { + function onContextRestore( /* event */ ) { - const vector = new Vector4(); + console.log( 'THREE.WebGLRenderer: Context Restored.' ); - const skinWeight = this.geometry.attributes.skinWeight; + _isContextLost = false; - for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { + const infoAutoReset = info.autoReset; + const shadowMapEnabled = shadowMap.enabled; + const shadowMapAutoUpdate = shadowMap.autoUpdate; + const shadowMapNeedsUpdate = shadowMap.needsUpdate; + const shadowMapType = shadowMap.type; - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); + initGLContext(); - const scale = 1.0 / vector.manhattanLength(); + info.autoReset = infoAutoReset; + shadowMap.enabled = shadowMapEnabled; + shadowMap.autoUpdate = shadowMapAutoUpdate; + shadowMap.needsUpdate = shadowMapNeedsUpdate; + shadowMap.type = shadowMapType; - if ( scale !== Infinity ) { + } - vector.multiplyScalar( scale ); + function onMaterialDispose( event ) { - } else { + const material = event.target; - vector.set( 1, 0, 0, 0 ); // do something reasonable + material.removeEventListener( 'dispose', onMaterialDispose ); - } + deallocateMaterial( material ); - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); + } - } + // Buffer deallocation + + function deallocateMaterial( material ) { + + releaseMaterialProgramReferences( material ); + + properties.remove( material ); } - updateMatrixWorld( force ) { - super.updateMatrixWorld( force ); + function releaseMaterialProgramReferences( material ) { - if ( this.bindMode === 'attached' ) { + const programs = properties.get( material ).programs; - this.bindMatrixInverse.copy( this.matrixWorld ).invert(); + if ( programs !== undefined ) { - } else if ( this.bindMode === 'detached' ) { + programs.forEach( function ( program ) { - this.bindMatrixInverse.copy( this.bindMatrix ).invert(); + programCache.releaseProgram( program ); - } else { + } ); - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); + if ( material.isShaderMaterial ) { + + programCache.releaseShaderCache( material ); + + } } } - boneTransform( index, target ) { + // Buffer rendering - const skeleton = this.skeleton; - const geometry = this.geometry; + this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); - _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); + if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - _basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix ); + const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - target.set( 0, 0, 0 ); + const program = setProgram( camera, scene, geometry, material, object ); - for ( let i = 0; i < 4; i ++ ) { + state.setMaterial( material, frontFaceCW ); - const weight = _skinWeight.getComponent( i ); + // - if ( weight !== 0 ) { + let index = geometry.index; + const position = geometry.attributes.position; - const boneIndex = _skinIndex.getComponent( i ); + // - _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); + if ( index === null ) { - target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); + if ( position === undefined || position.count === 0 ) return; - } + } else if ( index.count === 0 ) { + + return; } - return target.applyMatrix4( this.bindMatrixInverse ); + // - } + let rangeFactor = 1; -} + if ( material.wireframe === true ) { -SkinnedMesh.prototype.isSkinnedMesh = true; + index = geometries.getWireframeAttribute( geometry ); + rangeFactor = 2; -class Bone extends Object3D { + } - constructor() { + bindingStates.setup( object, material, program, geometry, index ); - super(); + let attribute; + let renderer = bufferRenderer; - this.type = 'Bone'; + if ( index !== null ) { - } + attribute = attributes.get( index ); -} + renderer = indexedBufferRenderer; + renderer.setIndex( attribute ); -Bone.prototype.isBone = true; + } -const _offsetMatrix = /*@__PURE__*/ new Matrix4(); -const _identityMatrix = /*@__PURE__*/ new Matrix4(); + // -class Skeleton { + const dataCount = ( index !== null ) ? index.count : position.count; - constructor( bones = [], boneInverses = [] ) { + const rangeStart = geometry.drawRange.start * rangeFactor; + const rangeCount = geometry.drawRange.count * rangeFactor; - this.uuid = generateUUID(); + const groupStart = group !== null ? group.start * rangeFactor : 0; + const groupCount = group !== null ? group.count * rangeFactor : Infinity; - this.bones = bones.slice( 0 ); - this.boneInverses = boneInverses; - this.boneMatrices = null; + const drawStart = Math.max( rangeStart, groupStart ); + const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - this.boneTexture = null; - this.boneTextureSize = 0; + const drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - this.frame = - 1; + if ( drawCount === 0 ) return; - this.init(); + // - } + if ( object.isMesh ) { - init() { + if ( material.wireframe === true ) { - const bones = this.bones; - const boneInverses = this.boneInverses; + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( 1 ); - this.boneMatrices = new Float32Array( bones.length * 16 ); + } else { - // calculate inverse bone matrices if necessary + renderer.setMode( 4 ); - if ( boneInverses.length === 0 ) { + } - this.calculateInverses(); + } else if ( object.isLine ) { - } else { + let lineWidth = material.linewidth; - // handle special case + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - if ( bones.length !== boneInverses.length ) { + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); + if ( object.isLineSegments ) { - this.boneInverses = []; + renderer.setMode( 1 ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } else if ( object.isLineLoop ) { - this.boneInverses.push( new Matrix4() ); + renderer.setMode( 2 ); - } + } else { + + renderer.setMode( 3 ); } - } + } else if ( object.isPoints ) { - } + renderer.setMode( 0 ); - calculateInverses() { + } else if ( object.isSprite ) { - this.boneInverses.length = 0; + renderer.setMode( 4 ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } - const inverse = new Matrix4(); + if ( object.isInstancedMesh ) { - if ( this.bones[ i ] ) { + renderer.renderInstances( drawStart, drawCount, object.count ); - inverse.copy( this.bones[ i ].matrixWorld ).invert(); + } else if ( geometry.isInstancedBufferGeometry ) { - } + const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount ); - this.boneInverses.push( inverse ); + renderer.renderInstances( drawStart, drawCount, instanceCount ); + + } else { + + renderer.render( drawStart, drawCount ); } - } + }; - pose() { + // Compile - // recover the bind-time world matrices + this.compile = function ( scene, camera ) { - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + currentRenderState = renderStates.get( scene ); + currentRenderState.init(); - const bone = this.bones[ i ]; + renderStateStack.push( currentRenderState ); - if ( bone ) { + scene.traverseVisible( function ( object ) { - bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); + if ( object.isLight && object.layers.test( camera.layers ) ) { - } + currentRenderState.pushLight( object ); - } + if ( object.castShadow ) { - // compute the local matrices, positions, rotations and scales + currentRenderState.pushShadow( object ); - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + } - const bone = this.bones[ i ]; + } - if ( bone ) { + } ); - if ( bone.parent && bone.parent.isBone ) { + currentRenderState.setupLights( _this.physicallyCorrectLights ); - bone.matrix.copy( bone.parent.matrixWorld ).invert(); - bone.matrix.multiply( bone.matrixWorld ); + scene.traverse( function ( object ) { - } else { + const material = object.material; - bone.matrix.copy( bone.matrixWorld ); + if ( material ) { - } + if ( Array.isArray( material ) ) { - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + for ( let i = 0; i < material.length; i ++ ) { - } + const material2 = material[ i ]; - } + getProgram( material2, scene, object ); - } + } - update() { + } else { - const bones = this.bones; - const boneInverses = this.boneInverses; - const boneMatrices = this.boneMatrices; - const boneTexture = this.boneTexture; + getProgram( material, scene, object ); - // flatten bone matrices to array + } - for ( let i = 0, il = bones.length; i < il; i ++ ) { + } - // compute the offset between the current and the original transform + } ); - const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; + renderStateStack.pop(); + currentRenderState = null; - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); + }; - } + // Animation Loop - if ( boneTexture !== null ) { + let onAnimationFrameCallback = null; - boneTexture.needsUpdate = true; + function onAnimationFrame( time ) { - } + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } - clone() { + function onXRSessionStart() { - return new Skeleton( this.bones, this.boneInverses ); + animation.stop(); } - getBoneByName( name ) { + function onXRSessionEnd() { - for ( let i = 0, il = this.bones.length; i < il; i ++ ) { + animation.start(); - const bone = this.bones[ i ]; + } - if ( bone.name === name ) { + const animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - return bone; + if ( typeof self !== 'undefined' ) animation.setContext( self ); - } + this.setAnimationLoop = function ( callback ) { - } + onAnimationFrameCallback = callback; + xr.setAnimationLoop( callback ); - return undefined; + ( callback === null ) ? animation.stop() : animation.start(); - } + }; - dispose( ) { + xr.addEventListener( 'sessionstart', onXRSessionStart ); + xr.addEventListener( 'sessionend', onXRSessionEnd ); - if ( this.boneTexture !== null ) { + // Rendering - this.boneTexture.dispose(); + this.render = function ( scene, camera ) { - this.boneTexture = null; + if ( camera !== undefined && camera.isCamera !== true ) { - } + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - } + } - fromJSON( json, bones ) { + if ( _isContextLost === true ) return; - this.uuid = json.uuid; + // update scene graph - for ( let i = 0, l = json.bones.length; i < l; i ++ ) { + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - const uuid = json.bones[ i ]; - let bone = bones[ uuid ]; + // update camera matrices and frustum - if ( bone === undefined ) { + if ( camera.parent === null ) camera.updateMatrixWorld(); - console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); - bone = new Bone(); + if ( xr.enabled === true && xr.isPresenting === true ) { - } + if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - this.bones.push( bone ); - this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); + camera = xr.getCamera(); // use XR camera for rendering } - this.init(); + // + if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - return this; + currentRenderState = renderStates.get( scene, renderStateStack.length ); + currentRenderState.init(); - } + renderStateStack.push( currentRenderState ); - toJSON() { + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); - const data = { - metadata: { - version: 4.5, - type: 'Skeleton', - generator: 'Skeleton.toJSON' - }, - bones: [], - boneInverses: [] - }; + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - data.uuid = this.uuid; + currentRenderList = renderLists.get( scene, renderListStack.length ); + currentRenderList.init(); - const bones = this.bones; - const boneInverses = this.boneInverses; + renderListStack.push( currentRenderList ); - for ( let i = 0, l = bones.length; i < l; i ++ ) { + projectObject( scene, camera, 0, _this.sortObjects ); - const bone = bones[ i ]; - data.bones.push( bone.uuid ); + currentRenderList.finish(); - const boneInverse = boneInverses[ i ]; - data.boneInverses.push( boneInverse.toArray() ); + if ( _this.sortObjects === true ) { + + currentRenderList.sort( _opaqueSort, _transparentSort ); } - return data; + // - } + if ( _clippingEnabled === true ) clipping.beginShadows(); -} + const shadowsArray = currentRenderState.state.shadowsArray; -const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); -const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); + shadowMap.render( shadowsArray, scene, camera ); -const _instanceIntersects = []; + if ( _clippingEnabled === true ) clipping.endShadows(); -const _mesh = /*@__PURE__*/ new Mesh(); + // -class InstancedMesh extends Mesh { + if ( this.info.autoReset === true ) this.info.reset(); - constructor( geometry, material, count ) { + // - super( geometry, material ); + background.render( currentRenderList, scene ); - this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 ); - this.instanceColor = null; + // render scene - this.count = count; + currentRenderState.setupLights( _this.physicallyCorrectLights ); - this.frustumCulled = false; + if ( camera.isArrayCamera ) { - } + const cameras = camera.cameras; - copy( source ) { + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - super.copy( source ); + const camera2 = cameras[ i ]; - this.instanceMatrix.copy( source.instanceMatrix ); + renderScene( currentRenderList, scene, camera2, camera2.viewport ); - if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); + } - this.count = source.count; + } else { - return this; + renderScene( currentRenderList, scene, camera ); - } + } - getColorAt( index, color ) { + // - color.fromArray( this.instanceColor.array, index * 3 ); + if ( _currentRenderTarget !== null ) { - } + // resolve multisample renderbuffers to a single-sample texture if necessary - getMatrixAt( index, matrix ) { + textures.updateMultisampleRenderTarget( _currentRenderTarget ); - matrix.fromArray( this.instanceMatrix.array, index * 16 ); + // Generate mipmap if we're using any kind of mipmap filtering - } + textures.updateRenderTargetMipmap( _currentRenderTarget ); - raycast( raycaster, intersects ) { + } - const matrixWorld = this.matrixWorld; - const raycastTimes = this.count; + // - _mesh.geometry = this.geometry; - _mesh.material = this.material; + if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - if ( _mesh.material === undefined ) return; + // _gl.finish(); - for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { + bindingStates.resetDefaultState(); + _currentMaterialId = - 1; + _currentCamera = null; - // calculate the world matrix for each instance + renderStateStack.pop(); - this.getMatrixAt( instanceId, _instanceLocalMatrix ); + if ( renderStateStack.length > 0 ) { - _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); + currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; - // the mesh represents this single instance + } else { - _mesh.matrixWorld = _instanceWorldMatrix; + currentRenderState = null; - _mesh.raycast( raycaster, _instanceIntersects ); + } - // process the result of raycast + renderListStack.pop(); - for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { + if ( renderListStack.length > 0 ) { - const intersect = _instanceIntersects[ i ]; - intersect.instanceId = instanceId; - intersect.object = this; - intersects.push( intersect ); + currentRenderList = renderListStack[ renderListStack.length - 1 ]; - } + } else { - _instanceIntersects.length = 0; + currentRenderList = null; } - } + }; - setColorAt( index, color ) { + function projectObject( object, camera, groupOrder, sortObjects ) { - if ( this.instanceColor === null ) { + if ( object.visible === false ) return; - this.instanceColor = new BufferAttribute( new Float32Array( this.count * 3 ), 3 ); + const visible = object.layers.test( camera.layers ); - } + if ( visible ) { - color.toArray( this.instanceColor.array, index * 3 ); + if ( object.isGroup ) { - } + groupOrder = object.renderOrder; - setMatrixAt( index, matrix ) { + } else if ( object.isLOD ) { - matrix.toArray( this.instanceMatrix.array, index * 16 ); + if ( object.autoUpdate === true ) object.update( camera ); - } + } else if ( object.isLight ) { - updateMorphTargets() { + currentRenderState.pushLight( object ); - } + if ( object.castShadow ) { - dispose() { + currentRenderState.pushShadow( object ); - this.dispatchEvent( { type: 'dispose' } ); + } - } + } else if ( object.isSprite ) { -} + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { -InstancedMesh.prototype.isInstancedMesh = true; + if ( sortObjects ) { -/** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); -class LineBasicMaterial extends Material { + } - constructor( parameters ) { + const geometry = objects.update( object ); + const material = object.material; - super(); + if ( material.visible ) { - this.type = 'LineBasicMaterial'; + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - this.color = new Color( 0xffffff ); + } - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; + } - this.morphTargets = false; + } else if ( object.isMesh || object.isLine || object.isPoints ) { - this.setValues( parameters ); + if ( object.isSkinnedMesh ) { - } + // update skeleton only once in a frame + if ( object.skeleton.frame !== info.render.frame ) { - copy( source ) { + object.skeleton.update(); + object.skeleton.frame = info.render.frame; - super.copy( source ); + } - this.color.copy( source.color ); + } - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - this.morphTargets = source.morphTargets; + if ( sortObjects ) { - return this; + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - } + } -} + const geometry = objects.update( object ); + const material = object.material; -LineBasicMaterial.prototype.isLineBasicMaterial = true; + if ( Array.isArray( material ) ) { -const _start$1 = /*@__PURE__*/ new Vector3(); -const _end$1 = /*@__PURE__*/ new Vector3(); -const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); -const _ray$1 = /*@__PURE__*/ new Ray(); -const _sphere$1 = /*@__PURE__*/ new Sphere(); + const groups = geometry.groups; -class Line extends Object3D { + for ( let i = 0, l = groups.length; i < l; i ++ ) { - constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - super(); + if ( groupMaterial && groupMaterial.visible ) { - this.type = 'Line'; + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - this.geometry = geometry; - this.material = material; + } - this.updateMorphTargets(); + } - } + } else if ( material.visible ) { - copy( source ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - super.copy( source ); + } - this.material = source.material; - this.geometry = source.geometry; + } - return this; + } - } + } - computeLineDistances() { + const children = object.children; - const geometry = this.geometry; + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( geometry.isBufferGeometry ) { + projectObject( children[ i ], camera, groupOrder, sortObjects ); - // we assume non-indexed geometry + } - if ( geometry.index === null ) { + } - const positionAttribute = geometry.attributes.position; - const lineDistances = [ 0 ]; + function renderScene( currentRenderList, scene, camera, viewport ) { - for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; + const transparentObjects = currentRenderList.transparent; - _start$1.fromBufferAttribute( positionAttribute, i - 1 ); - _end$1.fromBufferAttribute( positionAttribute, i ); + currentRenderState.setupLightsView( camera ); - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start$1.distanceTo( _end$1 ); + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera ); - } + if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); + if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); + if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - } else { + // Ensure depth buffer writing is enabled so it can be cleared on next render - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + state.buffers.depth.setTest( true ); + state.buffers.depth.setMask( true ); + state.buffers.color.setMask( true ); - } + state.setPolygonOffset( false ); - } else if ( geometry.isGeometry ) { + } - console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + function renderTransmissionPass( opaqueObjects, scene, camera ) { - } + const isWebGL2 = capabilities.isWebGL2; - return this; + if ( _transmissionRenderTarget === null ) { - } + _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { + generateMipmaps: true, + type: utils.convert( HalfFloatType ) !== null ? HalfFloatType : UnsignedByteType, + minFilter: LinearMipmapLinearFilter, + samples: ( isWebGL2 && _antialias === true ) ? 4 : 0 + } ); - raycast( raycaster, intersects ) { + } - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Line.threshold; - const drawRange = geometry.drawRange; + _this.getDrawingBufferSize( _vector2 ); - // Checking boundingSphere distance to ray + if ( isWebGL2 ) { - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); - _sphere$1.copy( geometry.boundingSphere ); - _sphere$1.applyMatrix4( matrixWorld ); - _sphere$1.radius += threshold; + } else { - if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; + _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); + + } // - _inverseMatrix$1.copy( matrixWorld ).invert(); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); + const currentRenderTarget = _this.getRenderTarget(); + _this.setRenderTarget( _transmissionRenderTarget ); + _this.clear(); - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + // Turn off the features which can affect the frag color for opaque objects pass. + // Otherwise they are applied twice in opaque objects pass and transmission objects pass. + const currentToneMapping = _this.toneMapping; + _this.toneMapping = NoToneMapping; - const vStart = new Vector3(); - const vEnd = new Vector3(); - const interSegment = new Vector3(); - const interRay = new Vector3(); - const step = this.isLineSegments ? 2 : 1; + renderObjects( opaqueObjects, scene, camera ); - if ( geometry.isBufferGeometry ) { + _this.toneMapping = currentToneMapping; - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - if ( index !== null ) { + _this.setRenderTarget( currentRenderTarget ); - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + } - for ( let i = start, l = end - 1; i < l; i += step ) { + function renderObjects( renderList, scene, camera ) { - const a = index.getX( i ); - const b = index.getX( i + 1 ); + const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - vStart.fromBufferAttribute( positionAttribute, a ); - vEnd.fromBufferAttribute( positionAttribute, b ); + for ( let i = 0, l = renderList.length; i < l; i ++ ) { - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + const renderItem = renderList[ i ]; - if ( distSq > localThresholdSq ) continue; + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = overrideMaterial === null ? renderItem.material : overrideMaterial; + const group = renderItem.group; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + if ( object.layers.test( camera.layers ) ) { - const distance = raycaster.ray.origin.distanceTo( interRay ); + renderObject( object, scene, camera, geometry, material, group ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + } - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } - } ); + function renderObject( object, scene, camera, geometry, material, group ) { - } + object.onBeforeRender( _this, scene, camera, geometry, material, group ); - } else { + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); + material.onBeforeRender( _this, scene, camera, geometry, object, group ); - for ( let i = start, l = end - 1; i < l; i += step ) { + if ( material.transparent === true && material.side === DoubleSide ) { - vStart.fromBufferAttribute( positionAttribute, i ); - vEnd.fromBufferAttribute( positionAttribute, i + 1 ); + material.side = BackSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + material.side = FrontSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( distSq > localThresholdSq ) continue; + material.side = DoubleSide; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } else { - const distance = raycaster.ray.origin.distanceTo( interRay ); + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + object.onAfterRender( _this, scene, camera, geometry, material, group ); - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } - } ); + function getProgram( material, scene, object ) { - } + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - } + const materialProperties = properties.get( material ); - } else if ( geometry.isGeometry ) { + const lights = currentRenderState.state.lights; + const shadowsArray = currentRenderState.state.shadowsArray; - console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + const lightsStateVersion = lights.state.version; - } + const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); + const programCacheKey = programCache.getProgramCacheKey( parameters ); - } + let programs = materialProperties.programs; - updateMorphTargets() { + // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - const geometry = this.geometry; + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - if ( geometry.isBufferGeometry ) { + if ( programs === undefined ) { - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + // new material - if ( keys.length > 0 ) { + material.addEventListener( 'dispose', onMaterialDispose ); - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + programs = new Map(); + materialProperties.programs = programs; - if ( morphAttribute !== undefined ) { + } - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + let program = programs.get( programCacheKey ); - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + if ( program !== undefined ) { - const name = morphAttribute[ m ].name || String( m ); + // early out if program and light state is identical - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - } + updateCommonMaterialProperties( material, parameters ); - } + return program; } } else { - const morphTargets = geometry.morphTargets; + parameters.uniforms = programCache.getUniforms( material ); - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + material.onBuild( object, parameters, _this ); - console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + material.onBeforeCompile( parameters, _this ); - } + program = programCache.acquireProgram( parameters, programCacheKey ); + programs.set( programCacheKey, program ); + + materialProperties.uniforms = parameters.uniforms; } - } + const uniforms = materialProperties.uniforms; -} + if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { -Line.prototype.isLine = true; + uniforms.clippingPlanes = clipping.uniform; -const _start = /*@__PURE__*/ new Vector3(); -const _end = /*@__PURE__*/ new Vector3(); + } -class LineSegments extends Line { + updateCommonMaterialProperties( material, parameters ); - constructor( geometry, material ) { + // store the light setup it was created for - super( geometry, material ); + materialProperties.needsLights = materialNeedsLights( material ); + materialProperties.lightsStateVersion = lightsStateVersion; - this.type = 'LineSegments'; + if ( materialProperties.needsLights ) { - } + // wire up the material to this renderer's lighting state - computeLineDistances() { + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.lightProbe.value = lights.state.probe; + uniforms.directionalLights.value = lights.state.directional; + uniforms.directionalLightShadows.value = lights.state.directionalShadow; + uniforms.spotLights.value = lights.state.spot; + uniforms.spotLightShadows.value = lights.state.spotShadow; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; - const geometry = this.geometry; + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; + // TODO (abelnation): add area lights shadow info to uniforms - if ( geometry.isBufferGeometry ) { + } - // we assume non-indexed geometry + const progUniforms = program.getUniforms(); + const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - if ( geometry.index === null ) { + materialProperties.currentProgram = program; + materialProperties.uniformsList = uniformsList; - const positionAttribute = geometry.attributes.position; - const lineDistances = []; + return program; - for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { + } - _start.fromBufferAttribute( positionAttribute, i ); - _end.fromBufferAttribute( positionAttribute, i + 1 ); + function updateCommonMaterialProperties( material, parameters ) { - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); + const materialProperties = properties.get( material ); - } + materialProperties.outputEncoding = parameters.outputEncoding; + materialProperties.instancing = parameters.instancing; + materialProperties.skinning = parameters.skinning; + materialProperties.morphTargets = parameters.morphTargets; + materialProperties.morphNormals = parameters.morphNormals; + materialProperties.morphColors = parameters.morphColors; + materialProperties.morphTargetsCount = parameters.morphTargetsCount; + materialProperties.numClippingPlanes = parameters.numClippingPlanes; + materialProperties.numIntersection = parameters.numClipIntersection; + materialProperties.vertexAlphas = parameters.vertexAlphas; + materialProperties.vertexTangents = parameters.vertexTangents; + materialProperties.toneMapping = parameters.toneMapping; - geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + } - } else { + function setProgram( camera, scene, geometry, material, object ) { - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - } + textures.resetTextureUnits(); - } else if ( geometry.isGeometry ) { + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; + const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.encoding : LinearEncoding ); + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; + const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent; + const morphTargets = !! geometry.morphAttributes.position; + const morphNormals = !! geometry.morphAttributes.normal; + const morphColors = !! geometry.morphAttributes.color; + const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; + + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + const materialProperties = properties.get( material ); + const lights = currentRenderState.state.lights; - } + if ( _clippingEnabled === true ) { - return this; + if ( _localClippingEnabled === true || camera !== _currentCamera ) { - } + const useCache = + camera === _currentCamera && + material.id === _currentMaterialId; -} + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + clipping.setState( material, camera, useCache ); -LineSegments.prototype.isLineSegments = true; + } -class LineLoop extends Line { + } - constructor( geometry, material ) { + // - super( geometry, material ); + let needsProgramChange = false; - this.type = 'LineLoop'; + if ( material.version === materialProperties.__version ) { - } + if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { -} + needsProgramChange = true; -LineLoop.prototype.isLineLoop = true; + } else if ( materialProperties.outputEncoding !== encoding ) { -/** - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * alphaMap: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ + needsProgramChange = true; -class PointsMaterial extends Material { + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - constructor( parameters ) { + needsProgramChange = true; - super(); + } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - this.type = 'PointsMaterial'; + needsProgramChange = true; - this.color = new Color( 0xffffff ); + } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - this.map = null; + needsProgramChange = true; - this.alphaMap = null; + } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - this.size = 1; - this.sizeAttenuation = true; + needsProgramChange = true; - this.morphTargets = false; + } else if ( materialProperties.envMap !== envMap ) { - this.setValues( parameters ); + needsProgramChange = true; - } + } else if ( material.fog && materialProperties.fog !== fog ) { - copy( source ) { + needsProgramChange = true; - super.copy( source ); + } else if ( materialProperties.numClippingPlanes !== undefined && + ( materialProperties.numClippingPlanes !== clipping.numPlanes || + materialProperties.numIntersection !== clipping.numIntersection ) ) { - this.color.copy( source.color ); + needsProgramChange = true; - this.map = source.map; + } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - this.alphaMap = source.alphaMap; + needsProgramChange = true; - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; + } else if ( materialProperties.vertexTangents !== vertexTangents ) { - this.morphTargets = source.morphTargets; + needsProgramChange = true; - return this; + } else if ( materialProperties.morphTargets !== morphTargets ) { - } + needsProgramChange = true; -} + } else if ( materialProperties.morphNormals !== morphNormals ) { -PointsMaterial.prototype.isPointsMaterial = true; + needsProgramChange = true; -const _inverseMatrix = /*@__PURE__*/ new Matrix4(); -const _ray = /*@__PURE__*/ new Ray(); -const _sphere = /*@__PURE__*/ new Sphere(); -const _position$2 = /*@__PURE__*/ new Vector3(); + } else if ( materialProperties.morphColors !== morphColors ) { -class Points extends Object3D { + needsProgramChange = true; - constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { + } else if ( materialProperties.toneMapping !== toneMapping ) { - super(); + needsProgramChange = true; - this.type = 'Points'; + } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { - this.geometry = geometry; - this.material = material; + needsProgramChange = true; - this.updateMorphTargets(); + } - } + } else { - copy( source ) { + needsProgramChange = true; + materialProperties.__version = material.version; - super.copy( source ); + } - this.material = source.material; - this.geometry = source.geometry; + // - return this; + let program = materialProperties.currentProgram; - } + if ( needsProgramChange === true ) { - raycast( raycaster, intersects ) { + program = getProgram( material, scene, object ); - const geometry = this.geometry; - const matrixWorld = this.matrixWorld; - const threshold = raycaster.params.Points.threshold; - const drawRange = geometry.drawRange; + } - // Checking boundingSphere distance to ray + let refreshProgram = false; + let refreshMaterial = false; + let refreshLights = false; - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + const p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); - _sphere.radius += threshold; + if ( state.useProgram( program.program ) ) { - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; - // + } - _inverseMatrix.copy( matrixWorld ).invert(); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); + if ( material.id !== _currentMaterialId ) { - const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - const localThresholdSq = localThreshold * localThreshold; + _currentMaterialId = material.id; - if ( geometry.isBufferGeometry ) { + refreshMaterial = true; - const index = geometry.index; - const attributes = geometry.attributes; - const positionAttribute = attributes.position; + } - if ( index !== null ) { + if ( refreshProgram || _currentCamera !== camera ) { - const start = Math.max( 0, drawRange.start ); - const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); - for ( let i = start, il = end; i < il; i ++ ) { + if ( capabilities.logarithmicDepthBuffer ) { - const a = index.getX( i ); + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - _position$2.fromBufferAttribute( positionAttribute, a ); + } - testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - - } + if ( _currentCamera !== camera ) { - } else { + _currentCamera = camera; - const start = Math.max( 0, drawRange.start ); - const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: - for ( let i = start, l = end; i < l; i ++ ) { + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done - _position$2.fromBufferAttribute( positionAttribute, i ); + } - testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - } + if ( material.isShaderMaterial || + material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshStandardMaterial || + material.envMap ) { - } + const uCamPos = p_uniforms.map.cameraPosition; - } else { + if ( uCamPos !== undefined ) { - console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + uCamPos.setValue( _gl, + _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - } + } - } + } - updateMorphTargets() { + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial ) { - const geometry = this.geometry; + p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - if ( geometry.isBufferGeometry ) { + } - const morphAttributes = geometry.morphAttributes; - const keys = Object.keys( morphAttributes ); + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial || + material.isShadowMaterial || + object.isSkinnedMesh ) { - if ( keys.length > 0 ) { + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - const morphAttribute = morphAttributes[ keys[ 0 ] ]; + } - if ( morphAttribute !== undefined ) { + } - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + // skinning and morph target uniforms must be set even if material didn't change + // auto-setting of texture unit for bone and morph texture must go before other textures + // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures - for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + if ( object.isSkinnedMesh ) { - const name = morphAttribute[ m ].name || String( m ); + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + const skeleton = object.skeleton; - } + if ( skeleton ) { - } + if ( capabilities.floatVertexTextures ) { - } + if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - } else { + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); + p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - const morphTargets = geometry.morphTargets; + } else { - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + } } } - } + const morphAttributes = geometry.morphAttributes; -} + if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { -Points.prototype.isPoints = true; + morphtargets.update( object, geometry, material, program ); -function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { + } - const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - if ( rayPointDistanceSq < localThresholdSq ) { + if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - const intersectPoint = new Vector3(); + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); - _ray.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); + } - const distance = raycaster.ray.origin.distanceTo( intersectPoint ); + if ( refreshMaterial ) { - if ( distance < raycaster.near || distance > raycaster.far ) return; + p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - intersects.push( { + if ( materialProperties.needsLights ) { - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object + // the current material requires lighting info - } ); + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required - } + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); -} + } -class VideoTexture extends Texture { + // refresh uniforms common to several materials - constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + if ( fog && material.fog ) { - super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + materials.refreshFogUniforms( m_uniforms, fog ); - this.format = format !== undefined ? format : RGBFormat; + } - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - this.generateMipmaps = false; + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - const scope = this; + } - function updateVideo() { + if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - scope.needsUpdate = true; - video.requestVideoFrameCallback( updateVideo ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; } - if ( 'requestVideoFrameCallback' in video ) { + if ( material.isSpriteMaterial ) { - video.requestVideoFrameCallback( updateVideo ); + p_uniforms.setValue( _gl, 'center', object.center ); } - } + // common matrices - clone() { + p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); + p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - return new this.constructor( this.image ).copy( this ); + return program; } - update() { - - const video = this.image; - const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { + function markUniformsLightsNeedsUpdate( uniforms, value ) { - this.needsUpdate = true; + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; - } + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; } -} - -VideoTexture.prototype.isVideoTexture = true; - -class CompressedTexture extends Texture { - - constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + function materialNeedsLights( material ) { - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || + material.isMeshStandardMaterial || material.isShadowMaterial || + ( material.isShaderMaterial && material.lights === true ); - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + } - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) + this.getActiveCubeFace = function () { - this.flipY = false; + return _currentActiveCubeFace; - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + }; - this.generateMipmaps = false; + this.getActiveMipmapLevel = function () { - } + return _currentActiveMipmapLevel; -} + }; -CompressedTexture.prototype.isCompressedTexture = true; + this.getRenderTarget = function () { -class CanvasTexture extends Texture { + return _currentRenderTarget; - constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + }; - super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { - this.needsUpdate = true; + properties.get( renderTarget.texture ).__webglTexture = colorTexture; + properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; - } + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__hasExternalTextures = true; -} + if ( renderTargetProperties.__hasExternalTextures ) { -CanvasTexture.prototype.isCanvasTexture = true; + renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; -class DepthTexture extends Texture { + if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { - constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { + // The multisample_render_to_texture extension doesn't work properly if there + // are midframe flushes and an external depth buffer. Disable use of the extension. + if ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) { - format = format !== undefined ? format : DepthFormat; + console.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' ); + renderTargetProperties.__useRenderToTexture = false; - if ( format !== DepthFormat && format !== DepthStencilFormat ) { + } - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); + } } - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; + }; - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { - this.image = { width: width, height: height }; + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__webglFramebuffer = defaultFramebuffer; + renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + }; - this.flipY = false; - this.generateMipmaps = false; + this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - } + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; + let useDefaultFramebuffer = true; -} + if ( renderTarget ) { -DepthTexture.prototype.isDepthTexture = true; + const renderTargetProperties = properties.get( renderTarget ); -new Vector3(); -new Vector3(); -new Vector3(); -new Triangle(); + if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { -/** - * Port from https://github.com/mapbox/earcut (v2.2.2) - */ + // We need to make sure to rebind the framebuffer. + state.bindFramebuffer( 36160, null ); + useDefaultFramebuffer = false; -const Earcut = { + } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { - triangulate: function ( data, holeIndices, dim ) { + textures.setupRenderTarget( renderTarget ); - dim = dim || 2; + } else if ( renderTargetProperties.__hasExternalTextures ) { - const hasHoles = holeIndices && holeIndices.length; - const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; - let outerNode = linkedList( data, 0, outerLen, dim, true ); - const triangles = []; + // Color and depth texture must be rebound in order for the swapchain to update. + textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; + } - let minX, minY, maxX, maxY, x, y, invSize; + } - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); + let framebuffer = null; + let isCube = false; + let isRenderTarget3D = false; - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { + if ( renderTarget ) { - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; + const texture = renderTarget.texture; - for ( let i = dim; i < outerLen; i += dim ) { + if ( texture.isData3DTexture || texture.isDataArrayTexture ) { - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; + isRenderTarget3D = true; } - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; - - } - - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); + const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - return triangles; + if ( renderTarget.isWebGLCubeRenderTarget ) { - } + framebuffer = __webglFramebuffer[ activeCubeFace ]; + isCube = true; -}; + } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { -// create a circular doubly linked list from polygon points in the specified winding order -function linkedList( data, start, end, dim, clockwise ) { + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - let i, last; + } else { - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { + framebuffer = __webglFramebuffer; - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + } - } else { + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + } else { - } + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); + _currentScissorTest = _scissorTest; - if ( last && equals( last, last.next ) ) { + } - removeNode( last ); - last = last.next; + const framebufferBound = state.bindFramebuffer( 36160, framebuffer ); - } + if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { - return last; + state.drawBuffers( renderTarget, framebuffer ); -} + } -// eliminate colinear or duplicate points -function filterPoints( start, end ) { + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); - if ( ! start ) return start; - if ( ! end ) end = start; + if ( isCube ) { - let p = start, - again; - do { + const textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( 36160, 36064, 34069 + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - again = false; + } else if ( isRenderTarget3D ) { - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { + const textureProperties = properties.get( renderTarget.texture ); + const layer = activeCubeFace || 0; + _gl.framebufferTextureLayer( 36160, 36064, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; + } - } else { + _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - p = p.next; + }; - } + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { - } while ( again || p !== end ); + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - return end; + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; -} + } -// main ear slicing loop which triangulates a polygon (given as a linked list) -function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - if ( ! ear ) return; + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); + framebuffer = framebuffer[ activeCubeFaceIndex ]; - let stop = ear, - prev, next; + } - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { + if ( framebuffer ) { - prev = ear.prev; - next = ear.next; + state.bindFramebuffer( 36160, framebuffer ); - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { + try { - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; - removeNode( ear ); + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - continue; + } - } + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - ear = next; + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - // try filtering points and slicing again - if ( ! pass ) { + } - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - // if this didn't work, try curing all small self-intersections locally + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - } else if ( pass === 1 ) { + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); - ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); + } - // as a last resort, try splitting the remaining polygon into two + } finally { - } else if ( pass === 2 ) { + // restore framebuffer of current render target if necessary - splitEarcut( ear, triangles, dim, minX, minY, invSize ); + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( 36160, framebuffer ); } - break; - } - } - -} + }; -// check whether a polygon node forms a valid ear with adjacent nodes -function isEar( ear ) { + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - const a = ear.prev, - b = ear, - c = ear.next; + if ( texture.isFramebufferTexture !== true ) { - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + console.error( 'THREE.WebGLRenderer: copyFramebufferToTexture() can only be used with FramebufferTexture.' ); + return; - // now make sure we don't have other points inside the potential ear - let p = ear.next.next; + } - while ( p !== ear.prev ) { + const levelScale = Math.pow( 2, - level ); + const width = Math.floor( texture.image.width * levelScale ); + const height = Math.floor( texture.image.height * levelScale ); - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; + textures.setTexture2D( texture, 0 ); - } + _gl.copyTexSubImage2D( 3553, level, 0, 0, position.x, position.y, width, height ); - return true; + state.unbindTexture(); -} + }; -function isEarHashed( ear, minX, minY, invSize ) { + this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - const a = ear.prev, - b = ear, - c = ear.next; + const width = srcTexture.image.width; + const height = srcTexture.image.height; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + textures.setTexture2D( dstTexture, 0 ); - // triangle bbox; min & max are calculated like this for speed - const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); + // As another texture upload may have changed pixelStorei + // parameters, make sure they are correct for the dstTexture + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - // z-order range for the current triangle bbox; - const minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); + if ( srcTexture.isDataTexture ) { - let p = ear.prevZ, - n = ear.nextZ; + _gl.texSubImage2D( 3553, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { + } else { - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + if ( srcTexture.isCompressedTexture ) { - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + _gl.compressedTexSubImage2D( 3553, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - } + } else { - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { + _gl.texSubImage2D( 3553, level, position.x, position.y, glFormat, glType, srcTexture.image ); - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + } - } + } - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( 3553 ); - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + state.unbindTexture(); - } + }; - return true; + this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { -} + if ( _this.isWebGL1Renderer ) { -// go through all polygon nodes and cure small local self-intersections -function cureLocalIntersections( start, triangles, dim ) { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); + return; - let p = start; - do { + } - const a = p.prev, - b = p.next.next; + const width = sourceBox.max.x - sourceBox.min.x + 1; + const height = sourceBox.max.y - sourceBox.min.y + 1; + const depth = sourceBox.max.z - sourceBox.min.z + 1; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); + let glTarget; - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { + if ( dstTexture.isData3DTexture ) { - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); + textures.setTexture3D( dstTexture, 0 ); + glTarget = 32879; - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); + } else if ( dstTexture.isDataArrayTexture ) { - p = start = b; + textures.setTexture2DArray( dstTexture, 0 ); + glTarget = 35866; + + } else { + + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); + return; } - p = p.next; + _gl.pixelStorei( 37440, dstTexture.flipY ); + _gl.pixelStorei( 37441, dstTexture.premultiplyAlpha ); + _gl.pixelStorei( 3317, dstTexture.unpackAlignment ); - } while ( p !== start ); + const unpackRowLen = _gl.getParameter( 3314 ); + const unpackImageHeight = _gl.getParameter( 32878 ); + const unpackSkipPixels = _gl.getParameter( 3316 ); + const unpackSkipRows = _gl.getParameter( 3315 ); + const unpackSkipImages = _gl.getParameter( 32877 ); - return filterPoints( p ); + const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; -} + _gl.pixelStorei( 3314, image.width ); + _gl.pixelStorei( 32878, image.height ); + _gl.pixelStorei( 3316, sourceBox.min.x ); + _gl.pixelStorei( 3315, sourceBox.min.y ); + _gl.pixelStorei( 32877, sourceBox.min.z ); -// try splitting polygon into two and triangulate them independently -function splitEarcut( start, triangles, dim, minX, minY, invSize ) { + if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { - // look for a valid diagonal that divides the polygon into two - let a = start; - do { + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - let b = a.next.next; - while ( b !== a.prev ) { + } else { - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { + if ( srcTexture.isCompressedTexture ) { - // split the polygon in two by the diagonal - let c = splitPolygon( a, b ); + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); + _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); + } else { - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); } - b = b.next; - } - a = a.next; - - } while ( a !== start ); + _gl.pixelStorei( 3314, unpackRowLen ); + _gl.pixelStorei( 32878, unpackImageHeight ); + _gl.pixelStorei( 3316, unpackSkipPixels ); + _gl.pixelStorei( 3315, unpackSkipRows ); + _gl.pixelStorei( 32877, unpackSkipImages ); -} + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); -// link every hole into the outer loop, producing a single-ring polygon without holes -function eliminateHoles( data, holeIndices, outerNode, dim ) { + state.unbindTexture(); - const queue = []; - let i, len, start, end, list; + }; - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { + this.initTexture = function ( texture ) { - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); + textures.setTexture2D( texture, 0 ); - } + state.unbindTexture(); - queue.sort( compareX ); + }; - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { + this.resetState = function () { - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); + _currentActiveCubeFace = 0; + _currentActiveMipmapLevel = 0; + _currentRenderTarget = null; - } + state.reset(); + bindingStates.reset(); - return outerNode; + }; -} + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { -function compareX( a, b ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - return a.x - b.x; + } } -// find a bridge between vertices that connects hole with an outer ring and and link it -function eliminateHole( hole, outerNode ) { +WebGLRenderer.prototype.isWebGLRenderer = true; - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { +class WebGL1Renderer extends WebGLRenderer {} - const b = splitPolygon( outerNode, hole ); +WebGL1Renderer.prototype.isWebGL1Renderer = true; - // filter collinear points around the cuts - filterPoints( outerNode, outerNode.next ); - filterPoints( b, b.next ); +class Scene extends Object3D { - } + constructor() { -} + super(); -// David Eberly's algorithm for finding a bridge between hole and outer polygon -function findHoleBridge( hole, outerNode ) { + this.type = 'Scene'; - let p = outerNode; - const hx = hole.x; - const hy = hole.y; - let qx = - Infinity, m; + this.background = null; + this.environment = null; + this.fog = null; - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { + this.overrideMaterial = null; - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { + this.autoUpdate = true; // checked by the renderer - const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - qx = x; - if ( x === hx ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; + } - } + } - m = p.x < p.next.x ? p : p.next; + copy( source, recursive ) { - } + super.copy( source, recursive ); - } + if ( source.background !== null ) this.background = source.background.clone(); + if ( source.environment !== null ) this.environment = source.environment.clone(); + if ( source.fog !== null ) this.fog = source.fog.clone(); - p = p.next; + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - } while ( p !== outerNode ); + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - if ( ! m ) return null; + return this; - if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint + } - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point + toJSON( meta ) { - const stop = m, - mx = m.x, - my = m.y; - let tanMin = Infinity, tan; + const data = super.toJSON( meta ); - p = m; + if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); - do { + return data; - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { + } - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential +} - if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { +Scene.prototype.isScene = true; - m = p; - tanMin = tan; +class InterleavedBuffer { - } + constructor( array, stride ) { - } + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; - p = p.next; + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; - } while ( p !== stop ); + this.version = 0; - return m; + this.uuid = generateUUID(); -} + } -// whether sector in vertex m contains sector in vertex p in the same coordinates -function sectorContainsSector( m, p ) { + onUploadCallback() {} - return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; + set needsUpdate( value ) { -} + if ( value === true ) this.version ++; -// interlink polygon nodes in z-order -function indexCurve( start, minX, minY, invSize ) { + } - let p = start; - do { + setUsage( value ) { - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; + this.usage = value; - } while ( p !== start ); + return this; - p.prevZ.nextZ = null; - p.prevZ = null; + } - sortLinked( p ); + copy( source ) { -} + this.array = new source.array.constructor( source.array ); + this.count = source.count; + this.stride = source.stride; + this.usage = source.usage; -// Simon Tatham's linked list merge sort algorithm -// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html -function sortLinked( list ) { + return this; - let i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; + } - do { + copyAt( index1, attribute, index2 ) { - p = list; - list = null; - tail = null; - numMerges = 0; + index1 *= this.stride; + index2 *= attribute.stride; - while ( p ) { + for ( let i = 0, l = this.stride; i < l; i ++ ) { - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - pSize ++; - q = q.nextZ; - if ( ! q ) break; + } - } + return this; - qSize = inSize; + } - while ( pSize > 0 || ( qSize > 0 && q ) ) { + set( value, offset = 0 ) { - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { + this.array.set( value, offset ); - e = p; - p = p.nextZ; - pSize --; + return this; - } else { + } - e = q; - q = q.nextZ; - qSize --; + clone( data ) { - } + if ( data.arrayBuffers === undefined ) { - if ( tail ) tail.nextZ = e; - else list = e; + data.arrayBuffers = {}; - e.prevZ = tail; - tail = e; + } - } + if ( this.array.buffer._uuid === undefined ) { - p = q; + this.array.buffer._uuid = generateUUID(); } - tail.nextZ = null; - inSize *= 2; + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - } while ( numMerges > 1 ); + data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - return list; + } -} + const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); -// z-order of a point given coords and inverse of the longer side of data bbox -function zOrder( x, y, minX, minY, invSize ) { + const ib = new this.constructor( array, this.stride ); + ib.setUsage( this.usage ); - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; + return ib; - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; + } - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; + onUpload( callback ) { - return x | ( y << 1 ); + this.onUploadCallback = callback; -} + return this; -// find the leftmost node of a polygon ring -function getLeftmost( start ) { + } - let p = start, - leftmost = start; - do { + toJSON( data ) { - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; + if ( data.arrayBuffers === undefined ) { - } while ( p !== start ); + data.arrayBuffers = {}; - return leftmost; + } -} + // generate UUID for array buffer if necessary -// check if a point lies within a convex triangle -function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { + if ( this.array.buffer._uuid === undefined ) { - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; + this.array.buffer._uuid = generateUUID(); -} + } -// check if a diagonal between two polygon nodes is valid (lies in polygon interior) -function isValidDiagonal( a, b ) { + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges - ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible - ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors - equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case + data.arrayBuffers[ this.array.buffer._uuid ] = Array.prototype.slice.call( new Uint32Array( this.array.buffer ) ); -} + } -// signed area of a triangle -function area( p, q, r ) { + // - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); + return { + uuid: this.uuid, + buffer: this.array.buffer._uuid, + type: this.array.constructor.name, + stride: this.stride + }; + + } } -// check if two points are equal -function equals( p1, p2 ) { +InterleavedBuffer.prototype.isInterleavedBuffer = true; - return p1.x === p2.x && p1.y === p2.y; +const _vector$6 = /*@__PURE__*/ new Vector3(); -} +class InterleavedBufferAttribute { -// check if two segments intersect -function intersects( p1, q1, p2, q2 ) { + constructor( interleavedBuffer, itemSize, offset, normalized = false ) { - const o1 = sign( area( p1, q1, p2 ) ); - const o2 = sign( area( p1, q1, q2 ) ); - const o3 = sign( area( p2, q2, p1 ) ); - const o4 = sign( area( p2, q2, q1 ) ); + this.name = ''; - if ( o1 !== o2 && o3 !== o4 ) return true; // general case + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; - if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 - if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 - if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 - if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + this.normalized = normalized === true; - return false; + } -} + get count() { -// for collinear points p, q, r, check if point q lies on segment pr -function onSegment( p, q, r ) { + return this.data.count; - return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); + } -} + get array() { -function sign( num ) { + return this.data.array; - return num > 0 ? 1 : num < 0 ? - 1 : 0; + } -} + set needsUpdate( value ) { -// check if a polygon diagonal intersects any polygon segments -function intersectsPolygon( a, b ) { + this.data.needsUpdate = value; - let p = a; - do { + } - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; + applyMatrix4( m ) { - } while ( p !== a ); + for ( let i = 0, l = this.data.count; i < l; i ++ ) { - return false; + _vector$6.fromBufferAttribute( this, i ); -} + _vector$6.applyMatrix4( m ); -// check if a polygon diagonal is locally inside the polygon -function locallyInside( a, b ) { + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; + } -} + return this; -// check if the middle point of a polygon diagonal is inside the polygon -function middleInside( a, b ) { + } - let p = a, - inside = false; - const px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { + applyNormalMatrix( m ) { - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; + for ( let i = 0, l = this.count; i < l; i ++ ) { - } while ( p !== a ); + _vector$6.fromBufferAttribute( this, i ); - return inside; + _vector$6.applyNormalMatrix( m ); -} + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); -// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; -// if one belongs to the outer ring and another to a hole, it merges it into a single ring -function splitPolygon( a, b ) { + } - const a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; + return this; - a.next = b; - b.prev = a; + } - a2.next = an; - an.prev = a2; + transformDirection( m ) { - b2.next = a2; - a2.prev = b2; + for ( let i = 0, l = this.count; i < l; i ++ ) { - bp.next = b2; - b2.prev = bp; + _vector$6.fromBufferAttribute( this, i ); - return b2; + _vector$6.transformDirection( m ); -} + this.setXYZ( i, _vector$6.x, _vector$6.y, _vector$6.z ); -// create a node and optionally link it with previous one (in a circular doubly linked list) -function insertNode( i, x, y, last ) { + } - const p = new Node( i, x, y ); + return this; - if ( ! last ) { + } - p.prev = p; - p.next = p; + setX( index, x ) { - } else { + this.data.array[ index * this.data.stride + this.offset ] = x; - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; + return this; } - return p; + setY( index, y ) { -} + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; -function removeNode( p ) { + return this; - p.next.prev = p.prev; - p.prev.next = p.next; + } - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; + setZ( index, z ) { -} + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; -function Node( i, x, y ) { + return this; - // vertex index in coordinates array - this.i = i; + } - // vertex coordinates - this.x = x; - this.y = y; + setW( index, w ) { - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - // z-order curve value - this.z = null; + return this; - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; + } - // indicates whether this is a steiner point - this.steiner = false; + getX( index ) { -} + return this.data.array[ index * this.data.stride + this.offset ]; -function signedArea( data, start, end, dim ) { + } - let sum = 0; - for ( let i = start, j = end - dim; i < end; i += dim ) { + getY( index ) { - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; + return this.data.array[ index * this.data.stride + this.offset + 1 ]; } - return sum; + getZ( index ) { -} + return this.data.array[ index * this.data.stride + this.offset + 2 ]; -class ShapeUtils { + } - // calculate area of the contour polygon + getW( index ) { - static area( contour ) { + return this.data.array[ index * this.data.stride + this.offset + 3 ]; - const n = contour.length; - let a = 0.0; + } - for ( let p = n - 1, q = 0; q < n; p = q ++ ) { + setXY( index, x, y ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + index = index * this.data.stride + this.offset; - } + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; - return a * 0.5; + return this; } - static isClockWise( pts ) { + setXYZ( index, x, y, z ) { - return ShapeUtils.area( pts ) < 0; + index = index * this.data.stride + this.offset; - } + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; - static triangulateShape( contour, holes ) { + return this; - const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - const holeIndices = []; // array of hole indices - const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + } - removeDupEndPts( contour ); - addContour( vertices, contour ); + setXYZW( index, x, y, z, w ) { - // + index = index * this.data.stride + this.offset; - let holeIndex = contour.length; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; - holes.forEach( removeDupEndPts ); + return this; - for ( let i = 0; i < holes.length; i ++ ) { + } - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); + clone( data ) { - } + if ( data === undefined ) { - // + console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interlaved buffer attribute will deinterleave buffer data.' ); - const triangles = Earcut.triangulate( vertices, holeIndices ); + const array = []; - // + for ( let i = 0; i < this.count; i ++ ) { - for ( let i = 0; i < triangles.length; i += 3 ) { + const index = i * this.data.stride + this.offset; - faces.push( triangles.slice( i, i + 3 ) ); + for ( let j = 0; j < this.itemSize; j ++ ) { - } + array.push( this.data.array[ index + j ] ); - return faces; + } - } + } -} + return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); -function removeDupEndPts( points ) { + } else { - const l = points.length; + if ( data.interleavedBuffers === undefined ) { - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + data.interleavedBuffers = {}; - points.pop(); + } - } + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { -} + data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); -function addContour( vertices, contour ) { + } - for ( let i = 0; i < contour.length; i ++ ) { + return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); + } } -} + toJSON( data ) { -/** - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ + if ( data === undefined ) { -class ExtrudeGeometry extends BufferGeometry { + console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interlaved buffer attribute will deinterleave buffer data.' ); - constructor( shapes, options ) { + const array = []; - super(); + for ( let i = 0; i < this.count; i ++ ) { - this.type = 'ExtrudeGeometry'; + const index = i * this.data.stride + this.offset; - this.parameters = { - shapes: shapes, - options: options - }; + for ( let j = 0; j < this.itemSize; j ++ ) { - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + array.push( this.data.array[ index + j ] ); - const scope = this; + } - const verticesArray = []; - const uvArray = []; + } - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + // deinterleave data and save it as an ordinary buffer attribute for now - const shape = shapes[ i ]; - addShape( shape ); + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: array, + normalized: this.normalized + }; - } + } else { - // build geometry + // save as true interlaved attribtue - this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); + if ( data.interleavedBuffers === undefined ) { - this.computeVertexNormals(); + data.interleavedBuffers = {}; - // functions + } - function addShape( shape ) { + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - const placeholder = []; + data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - // options + } - const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - const steps = options.steps !== undefined ? options.steps : 1; - let depth = options.depth !== undefined ? options.depth : 100; + return { + isInterleavedBufferAttribute: true, + itemSize: this.itemSize, + data: this.data.uuid, + offset: this.offset, + normalized: this.normalized + }; - let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + } - const extrudePath = options.extrudePath; + } - const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; +} - // deprecated options +InterleavedBufferAttribute.prototype.isInterleavedBufferAttribute = true; - if ( options.amount !== undefined ) { +class SpriteMaterial extends Material { - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; + constructor( parameters ) { - } + super(); - // + this.type = 'SpriteMaterial'; - let extrudePts, extrudeByPath = false; - let splineTube, binormal, normal, position2; + this.color = new Color( 0xffffff ); - if ( extrudePath ) { + this.map = null; - extrudePts = extrudePath.getSpacedPoints( steps ); + this.alphaMap = null; - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + this.rotation = 0; - // SETUP TNB variables + this.sizeAttenuation = true; - // TODO1 - have a .isClosed in spline? + this.transparent = true; - splineTube = extrudePath.computeFrenetFrames( steps, false ); + this.setValues( parameters ); - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + } - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); + copy( source ) { - } + super.copy( source ); - // Safeguards if bevels are not enabled + this.color.copy( source.color ); - if ( ! bevelEnabled ) { + this.map = source.map; - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; + this.alphaMap = source.alphaMap; - } + this.rotation = source.rotation; - // Variables initialization + this.sizeAttenuation = source.sizeAttenuation; - const shapePoints = shape.extractPoints( curveSegments ); + return this; - let vertices = shapePoints.shape; - const holes = shapePoints.holes; + } - const reverse = ! ShapeUtils.isClockWise( vertices ); +} - if ( reverse ) { +SpriteMaterial.prototype.isSpriteMaterial = true; - vertices = vertices.reverse(); +let _geometry; - // Maybe we should also check if holes are in the opposite direction, just to be safe ... +const _intersectPoint = /*@__PURE__*/ new Vector3(); +const _worldScale = /*@__PURE__*/ new Vector3(); +const _mvPosition = /*@__PURE__*/ new Vector3(); - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { +const _alignedPosition = /*@__PURE__*/ new Vector2(); +const _rotatedPosition = /*@__PURE__*/ new Vector2(); +const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); - const ahole = holes[ h ]; +const _vA = /*@__PURE__*/ new Vector3(); +const _vB = /*@__PURE__*/ new Vector3(); +const _vC = /*@__PURE__*/ new Vector3(); - if ( ShapeUtils.isClockWise( ahole ) ) { +const _uvA = /*@__PURE__*/ new Vector2(); +const _uvB = /*@__PURE__*/ new Vector2(); +const _uvC = /*@__PURE__*/ new Vector2(); - holes[ h ] = ahole.reverse(); +class Sprite extends Object3D { - } + constructor( material ) { - } + super(); - } + this.type = 'Sprite'; + if ( _geometry === undefined ) { - const faces = ShapeUtils.triangulateShape( vertices, holes ); + _geometry = new BufferGeometry(); - /* Vertices */ + const float32Array = new Float32Array( [ + - 0.5, - 0.5, 0, 0, 0, + 0.5, - 0.5, 0, 1, 0, + 0.5, 0.5, 0, 1, 1, + - 0.5, 0.5, 0, 0, 1 + ] ); - const contour = vertices; // vertices has all points but contour has only points of circumference + const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); + _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); + _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - const ahole = holes[ h ]; + } - vertices = vertices.concat( ahole ); + this.geometry = _geometry; + this.material = ( material !== undefined ) ? material : new SpriteMaterial(); - } + this.center = new Vector2( 0.5, 0.5 ); + } - function scalePt2( pt, vec, size ) { + raycast( raycaster, intersects ) { - if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); + if ( raycaster.camera === null ) { - return vec.clone().multiplyScalar( size ).add( pt ); + console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - } + } - const vlen = vertices.length, flen = faces.length; + _worldScale.setFromMatrixScale( this.matrixWorld ); + _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); + this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - // Find directions for point movement + _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - function getBevelVec( inPt, inPrev, inNext ) { + _worldScale.multiplyScalar( - _mvPosition.z ); - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. + } - let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + const rotation = this.material.rotation; + let sin, cos; - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + if ( rotation !== 0 ) { - const v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - const v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; + cos = Math.cos( rotation ); + sin = Math.sin( rotation ); - const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + } - // check for collinear edges - const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + const center = this.center; - if ( Math.abs( collinear0 ) > Number.EPSILON ) { + transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - // not collinear + _uvA.set( 0, 0 ); + _uvB.set( 1, 0 ); + _uvC.set( 1, 1 ); - // length of vectors for normalizing + // check first triangle + let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); - const v_prev_len = Math.sqrt( v_prev_lensq ); - const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + if ( intersect === null ) { - // shift adjacent points by unit vectors to the left + // check second triangle + transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + _uvB.set( 0, 1 ); - const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); + if ( intersect === null ) { - const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + return; - // scaling factor for v_prev to intersection point + } - const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + } - // vector from inPt to intersection point + const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + if ( distance < raycaster.near || distance > raycaster.far ) return; - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { + intersects.push( { - return new Vector2( v_trans_x, v_trans_y ); + distance: distance, + point: _intersectPoint.clone(), + uv: Triangle.getUV( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), + face: null, + object: this - } else { + } ); - shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } - } + copy( source ) { - } else { + super.copy( source ); - // handle special case of collinear edges + if ( source.center !== undefined ) this.center.copy( source.center ); - let direction_eq = false; // assumes: opposite + this.material = source.material; - if ( v_prev_x > Number.EPSILON ) { + return this; - if ( v_next_x > Number.EPSILON ) { + } - direction_eq = true; +} - } +Sprite.prototype.isSprite = true; - } else { +function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - if ( v_prev_x < - Number.EPSILON ) { + // compute position in camera space + _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - if ( v_next_x < - Number.EPSILON ) { + // to check if rotation is not zero + if ( sin !== undefined ) { - direction_eq = true; + _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); + _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - } + } else { - } else { + _rotatedPosition.copy( _alignedPosition ); - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + } - direction_eq = true; - } + vertexPosition.copy( mvPosition ); + vertexPosition.x += _rotatedPosition.x; + vertexPosition.y += _rotatedPosition.y; - } + // transform to world space + vertexPosition.applyMatrix4( _viewWorldMatrix ); - } +} - if ( direction_eq ) { +const _basePosition = /*@__PURE__*/ new Vector3(); - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); +const _skinIndex = /*@__PURE__*/ new Vector4(); +const _skinWeight = /*@__PURE__*/ new Vector4(); - } else { +const _vector$5 = /*@__PURE__*/ new Vector3(); +const _matrix = /*@__PURE__*/ new Matrix4(); - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); +class SkinnedMesh extends Mesh { - } + constructor( geometry, material ) { - } + super( geometry, material ); - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + this.type = 'SkinnedMesh'; - } + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); + } - const contourMovements = []; + copy( source ) { - for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + super.copy( source ); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + this.bindMode = source.bindMode; + this.bindMatrix.copy( source.bindMatrix ); + this.bindMatrixInverse.copy( source.bindMatrixInverse ); - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + this.skeleton = source.skeleton; - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + return this; - } + } - const holesMovements = []; - let oneHoleMovements, verticesMovements = contourMovements.concat(); + bind( skeleton, bindMatrix ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + this.skeleton = skeleton; - const ahole = holes[ h ]; + if ( bindMatrix === undefined ) { - oneHoleMovements = []; + this.updateMatrixWorld( true ); - for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + this.skeleton.calculateInverses(); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + bindMatrix = this.matrixWorld; - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + } - } + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.copy( bindMatrix ).invert(); - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + } - } + pose() { + this.skeleton.pose(); - // Loop bevelSegments, 1 for the front, 1 for the back + } - for ( let b = 0; b < bevelSegments; b ++ ) { + normalizeSkinWeights() { - //for ( b = bevelSegments; b > 0; b -- ) { + const vector = new Vector4(); - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + const skinWeight = this.geometry.attributes.skinWeight; - // contract shape + for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - for ( let i = 0, il = contour.length; i < il; i ++ ) { + vector.fromBufferAttribute( skinWeight, i ); - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + const scale = 1.0 / vector.manhattanLength(); - v( vert.x, vert.y, - z ); + if ( scale !== Infinity ) { - } + vector.multiplyScalar( scale ); - // expand holes + } else { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + vector.set( 1, 0, 0, 0 ); // do something reasonable - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + } - for ( let i = 0, il = ahole.length; i < il; i ++ ) { + skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + } - v( vert.x, vert.y, - z ); + } - } + updateMatrixWorld( force ) { - } + super.updateMatrixWorld( force ); - } + if ( this.bindMode === 'attached' ) { - const bs = bevelSize + bevelOffset; + this.bindMatrixInverse.copy( this.matrixWorld ).invert(); - // Back facing vertices + } else if ( this.bindMode === 'detached' ) { - for ( let i = 0; i < vlen; i ++ ) { + this.bindMatrixInverse.copy( this.bindMatrix ).invert(); - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + } else { - if ( ! extrudeByPath ) { + console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - v( vert.x, vert.y, 0 ); + } - } else { + } - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + boneTransform( index, target ) { - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + const skeleton = this.skeleton; + const geometry = this.geometry; - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); + _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - v( position2.x, position2.y, position2.z ); + _basePosition.copy( target ).applyMatrix4( this.bindMatrix ); - } + target.set( 0, 0, 0 ); - } + for ( let i = 0; i < 4; i ++ ) { - // Add stepped vertices... - // Including front facing vertices + const weight = _skinWeight.getComponent( i ); - for ( let s = 1; s <= steps; s ++ ) { + if ( weight !== 0 ) { - for ( let i = 0; i < vlen; i ++ ) { + const boneIndex = _skinIndex.getComponent( i ); - const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + _matrix.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - if ( ! extrudeByPath ) { + target.addScaledVector( _vector$5.copy( _basePosition ).applyMatrix4( _matrix ), weight ); - v( vert.x, vert.y, depth / steps * s ); + } - } else { + } - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + return target.applyMatrix4( this.bindMatrixInverse ); - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + } - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); +} - v( position2.x, position2.y, position2.z ); +SkinnedMesh.prototype.isSkinnedMesh = true; - } +class Bone extends Object3D { - } + constructor() { - } + super(); + this.type = 'Bone'; - // Add bevel segments planes + } - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( let b = bevelSegments - 1; b >= 0; b -- ) { +} - const t = b / bevelSegments; - const z = bevelThickness * Math.cos( t * Math.PI / 2 ); - const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; +Bone.prototype.isBone = true; - // contract shape +class DataTexture extends Texture { - for ( let i = 0, il = contour.length; i < il; i ++ ) { + constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, encoding ) { - const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - } + this.image = { data: data, width: width, height: height }; - // expand holes + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + } - const ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; +} - for ( let i = 0, il = ahole.length; i < il; i ++ ) { +DataTexture.prototype.isDataTexture = true; - const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); +const _offsetMatrix = /*@__PURE__*/ new Matrix4(); +const _identityMatrix = /*@__PURE__*/ new Matrix4(); - if ( ! extrudeByPath ) { +class Skeleton { - v( vert.x, vert.y, depth + z ); + constructor( bones = [], boneInverses = [] ) { - } else { + this.uuid = generateUUID(); - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + this.bones = bones.slice( 0 ); + this.boneInverses = boneInverses; + this.boneMatrices = null; - } + this.boneTexture = null; + this.boneTextureSize = 0; - } + this.frame = - 1; - } + this.init(); - } + } - /* Faces */ + init() { - // Top and bottom faces + const bones = this.bones; + const boneInverses = this.boneInverses; - buildLidFaces(); + this.boneMatrices = new Float32Array( bones.length * 16 ); - // Sides faces + // calculate inverse bone matrices if necessary - buildSideFaces(); + if ( boneInverses.length === 0 ) { + this.calculateInverses(); - ///// Internal functions + } else { - function buildLidFaces() { + // handle special case - const start = verticesArray.length / 3; + if ( bones.length !== boneInverses.length ) { - if ( bevelEnabled ) { + console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); - let layer = 0; // steps + 1 - let offset = vlen * layer; + this.boneInverses = []; - // Bottom faces + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - for ( let i = 0; i < flen; i ++ ) { + this.boneInverses.push( new Matrix4() ); - const face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + } - } + } - layer = steps + bevelSegments * 2; - offset = vlen * layer; + } - // Top faces + } - for ( let i = 0; i < flen; i ++ ) { + calculateInverses() { - const face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + this.boneInverses.length = 0; - } + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - } else { + const inverse = new Matrix4(); - // Bottom faces + if ( this.bones[ i ] ) { - for ( let i = 0; i < flen; i ++ ) { + inverse.copy( this.bones[ i ].matrixWorld ).invert(); - const face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + } - } + this.boneInverses.push( inverse ); - // Top faces + } - for ( let i = 0; i < flen; i ++ ) { + } - const face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + pose() { - } + // recover the bind-time world matrices - } + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); + const bone = this.bones[ i ]; + + if ( bone ) { + + bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); } - // Create faces for the z-sides of the shape + } - function buildSideFaces() { + // compute the local matrices, positions, rotations and scales - const start = verticesArray.length / 3; - let layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + const bone = this.bones[ i ]; - const ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + if ( bone ) { - //, true - layeroffset += ahole.length; + if ( bone.parent && bone.parent.isBone ) { - } + bone.matrix.copy( bone.parent.matrixWorld ).invert(); + bone.matrix.multiply( bone.matrixWorld ); + } else { - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); + bone.matrix.copy( bone.matrixWorld ); + + } + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } - function sidewalls( contour, layeroffset ) { + } - let i = contour.length; + } - while ( -- i >= 0 ) { + update() { - const j = i; - let k = i - 1; - if ( k < 0 ) k = contour.length - 1; + const bones = this.bones; + const boneInverses = this.boneInverses; + const boneMatrices = this.boneMatrices; + const boneTexture = this.boneTexture; - //console.log('b', i,j, i-1, k,vertices.length); + // flatten bone matrices to array - for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { + for ( let i = 0, il = bones.length; i < il; i ++ ) { - const slen1 = vlen * s; - const slen2 = vlen * ( s + 1 ); + // compute the offset between the current and the original transform - const a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - f4( a, b, c, d ); + _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); + _offsetMatrix.toArray( boneMatrices, i * 16 ); - } + } - } + if ( boneTexture !== null ) { - } + boneTexture.needsUpdate = true; - function v( x, y, z ) { + } - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); + } - } + clone() { + return new Skeleton( this.bones, this.boneInverses ); - function f3( a, b, c ) { + } - addVertex( a ); - addVertex( b ); - addVertex( c ); + computeBoneTexture() { - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); + let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = ceilPowerOfTwo( size ); + size = Math.max( size, 4 ); - } + const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel + boneMatrices.set( this.boneMatrices ); // copy current values - function f4( a, b, c, d ) { + const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + boneTexture.needsUpdate = true; - addVertex( a ); - addVertex( b ); - addVertex( d ); + this.boneMatrices = boneMatrices; + this.boneTexture = boneTexture; + this.boneTextureSize = size; - addVertex( b ); - addVertex( c ); - addVertex( d ); + return this; + } - const nextIndex = verticesArray.length / 3; - const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + getBoneByName( name ) { - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); + const bone = this.bones[ i ]; + + if ( bone.name === name ) { + + return bone; } - function addVertex( index ) { + } - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); + return undefined; - } + } + dispose( ) { - function addUV( vector2 ) { + if ( this.boneTexture !== null ) { - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); + this.boneTexture.dispose(); - } + this.boneTexture = null; } } - toJSON() { + fromJSON( json, bones ) { - const data = BufferGeometry.prototype.toJSON.call( this ); + this.uuid = json.uuid; - const shapes = this.parameters.shapes; - const options = this.parameters.options; + for ( let i = 0, l = json.bones.length; i < l; i ++ ) { - return toJSON$1( shapes, options, data ); + const uuid = json.bones[ i ]; + let bone = bones[ uuid ]; - } + if ( bone === undefined ) { -} + console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); + bone = new Bone(); -const WorldUVGenerator = { + } - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { + this.bones.push( bone ); + this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; + } - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; + this.init(); - }, + return this; - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { + } - const a_x = vertices[ indexA * 3 ]; - const a_y = vertices[ indexA * 3 + 1 ]; - const a_z = vertices[ indexA * 3 + 2 ]; - const b_x = vertices[ indexB * 3 ]; - const b_y = vertices[ indexB * 3 + 1 ]; - const b_z = vertices[ indexB * 3 + 2 ]; - const c_x = vertices[ indexC * 3 ]; - const c_y = vertices[ indexC * 3 + 1 ]; - const c_z = vertices[ indexC * 3 + 2 ]; - const d_x = vertices[ indexD * 3 ]; - const d_y = vertices[ indexD * 3 + 1 ]; - const d_z = vertices[ indexD * 3 + 2 ]; + toJSON() { - if ( Math.abs( a_y - b_y ) < 0.01 ) { + const data = { + metadata: { + version: 4.5, + type: 'Skeleton', + generator: 'Skeleton.toJSON' + }, + bones: [], + boneInverses: [] + }; - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; + data.uuid = this.uuid; - } else { + const bones = this.bones; + const boneInverses = this.boneInverses; - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; + for ( let i = 0, l = bones.length; i < l; i ++ ) { + + const bone = bones[ i ]; + data.bones.push( bone.uuid ); + + const boneInverse = boneInverses[ i ]; + data.boneInverses.push( boneInverse.toArray() ); } + return data; + } -}; +} -function toJSON$1( shapes, options, data ) { +class InstancedBufferAttribute extends BufferAttribute { - data.shapes = []; + constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { - if ( Array.isArray( shapes ) ) { + if ( typeof normalized === 'number' ) { - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + meshPerAttribute = normalized; - const shape = shapes[ i ]; + normalized = false; - data.shapes.push( shape.uuid ); + console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); } - } else { + super( array, itemSize, normalized ); - data.shapes.push( shapes.uuid ); + this.meshPerAttribute = meshPerAttribute; } - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); + copy( source ) { - return data; + super.copy( source ); -} + this.meshPerAttribute = source.meshPerAttribute; -class ShapeGeometry extends BufferGeometry { + return this; - constructor( shapes, curveSegments = 12 ) { + } - super(); - this.type = 'ShapeGeometry'; + toJSON() { - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; + const data = super.toJSON(); - // buffers + data.meshPerAttribute = this.meshPerAttribute; - const indices = []; - const vertices = []; - const normals = []; - const uvs = []; + data.isInstancedBufferAttribute = true; - // helper variables + return data; - let groupStart = 0; - let groupCount = 0; + } - // allow single and array values for "shapes" parameter +} - if ( Array.isArray( shapes ) === false ) { +InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; - addShape( shapes ); +const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); +const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); - } else { +const _instanceIntersects = []; - for ( let i = 0; i < shapes.length; i ++ ) { +const _mesh = /*@__PURE__*/ new Mesh(); - addShape( shapes[ i ] ); +class InstancedMesh extends Mesh { - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support + constructor( geometry, material, count ) { - groupStart += groupCount; - groupCount = 0; + super( geometry, material ); - } + this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); + this.instanceColor = null; - } + this.count = count; - // build geometry + this.frustumCulled = false; - this.setIndex( indices ); - this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + } + copy( source ) { - // helper functions + super.copy( source ); - function addShape( shape ) { + this.instanceMatrix.copy( source.instanceMatrix ); - const indexOffset = vertices.length / 3; - const points = shape.extractPoints( curveSegments ); + if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); - let shapeVertices = points.shape; - const shapeHoles = points.holes; + this.count = source.count; - // check direction of vertices - - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { - - shapeVertices = shapeVertices.reverse(); - - } + return this; - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + } - const shapeHole = shapeHoles[ i ]; + getColorAt( index, color ) { - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + color.fromArray( this.instanceColor.array, index * 3 ); - shapeHoles[ i ] = shapeHole.reverse(); + } - } + getMatrixAt( index, matrix ) { - } + matrix.fromArray( this.instanceMatrix.array, index * 16 ); - const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); + } - // join vertices of inner and outer paths to a single array + raycast( raycaster, intersects ) { - for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + const matrixWorld = this.matrixWorld; + const raycastTimes = this.count; - const shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); + _mesh.geometry = this.geometry; + _mesh.material = this.material; - } + if ( _mesh.material === undefined ) return; - // vertices, normals, uvs + for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { + // calculate the world matrix for each instance - const vertex = shapeVertices[ i ]; + this.getMatrixAt( instanceId, _instanceLocalMatrix ); - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs + _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - } + // the mesh represents this single instance - // incides + _mesh.matrixWorld = _instanceWorldMatrix; - for ( let i = 0, l = faces.length; i < l; i ++ ) { + _mesh.raycast( raycaster, _instanceIntersects ); - const face = faces[ i ]; + // process the result of raycast - const a = face[ 0 ] + indexOffset; - const b = face[ 1 ] + indexOffset; - const c = face[ 2 ] + indexOffset; + for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - indices.push( a, b, c ); - groupCount += 3; + const intersect = _instanceIntersects[ i ]; + intersect.instanceId = instanceId; + intersect.object = this; + intersects.push( intersect ); } + _instanceIntersects.length = 0; + } } - toJSON() { - - const data = BufferGeometry.prototype.toJSON.call( this ); - - const shapes = this.parameters.shapes; + setColorAt( index, color ) { - return toJSON( shapes, data ); + if ( this.instanceColor === null ) { - } + this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); -} + } -function toJSON( shapes, data ) { + color.toArray( this.instanceColor.array, index * 3 ); - data.shapes = []; + } - if ( Array.isArray( shapes ) ) { + setMatrixAt( index, matrix ) { - for ( let i = 0, l = shapes.length; i < l; i ++ ) { + matrix.toArray( this.instanceMatrix.array, index * 16 ); - const shape = shapes[ i ]; + } - data.shapes.push( shape.uuid ); + updateMorphTargets() { - } + } - } else { + dispose() { - data.shapes.push( shapes.uuid ); + this.dispatchEvent( { type: 'dispose' } ); } - return data; - } -/** - * parameters = { - * color: - * } - */ +InstancedMesh.prototype.isInstancedMesh = true; -class ShadowMaterial extends Material { +class LineBasicMaterial extends Material { constructor( parameters ) { super(); - this.type = 'ShadowMaterial'; + this.type = 'LineBasicMaterial'; - this.color = new Color( 0x000000 ); - this.transparent = true; + this.color = new Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; this.setValues( parameters ); } + copy( source ) { super.copy( source ); this.color.copy( source.color ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + return this; } } -ShadowMaterial.prototype.isShadowMaterial = true; +LineBasicMaterial.prototype.isLineBasicMaterial = true; -class RawShaderMaterial extends ShaderMaterial { +const _start$1 = /*@__PURE__*/ new Vector3(); +const _end$1 = /*@__PURE__*/ new Vector3(); +const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); +const _ray$1 = /*@__PURE__*/ new Ray(); +const _sphere$1 = /*@__PURE__*/ new Sphere(); - constructor( parameters ) { +class Line extends Object3D { - super( parameters ); + constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { - this.type = 'RawShaderMaterial'; + super(); - } + this.type = 'Line'; -} + this.geometry = geometry; + this.material = material; -RawShaderMaterial.prototype.isRawShaderMaterial = true; + this.updateMorphTargets(); -/** - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ + } -class MeshStandardMaterial extends Material { + copy( source ) { - constructor( parameters ) { + super.copy( source ); - super(); + this.material = source.material; + this.geometry = source.geometry; - this.defines = { 'STANDARD': '' }; + return this; - this.type = 'MeshStandardMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 1.0; - this.metalness = 0.0; + computeLineDistances() { - this.map = null; + const geometry = this.geometry; - this.lightMap = null; - this.lightMapIntensity = 1.0; + if ( geometry.isBufferGeometry ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + // we assume non-indexed geometry - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + if ( geometry.index === null ) { - this.bumpMap = null; - this.bumpScale = 1; + const positionAttribute = geometry.attributes.position; + const lineDistances = [ 0 ]; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + _start$1.fromBufferAttribute( positionAttribute, i - 1 ); + _end$1.fromBufferAttribute( positionAttribute, i ); - this.roughnessMap = null; + lineDistances[ i ] = lineDistances[ i - 1 ]; + lineDistances[ i ] += _start$1.distanceTo( _end$1 ); - this.metalnessMap = null; + } - this.alphaMap = null; + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - this.envMap = null; - this.envMapIntensity = 1.0; + } else { - this.refractionRatio = 0.98; + console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } else if ( geometry.isGeometry ) { - this.flatShading = false; + console.error( 'THREE.Line.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.vertexTangents = false; + } - this.setValues( parameters ); + return this; } - copy( source ) { + raycast( raycaster, intersects ) { - super.copy( source ); + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Line.threshold; + const drawRange = geometry.drawRange; - this.defines = { 'STANDARD': '' }; + // Checking boundingSphere distance to ray - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - this.map = source.map; + _sphere$1.copy( geometry.boundingSphere ); + _sphere$1.applyMatrix4( matrixWorld ); + _sphere$1.radius += threshold; - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + // - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + _inverseMatrix$1.copy( matrixWorld ).invert(); + _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + const vStart = new Vector3(); + const vEnd = new Vector3(); + const interSegment = new Vector3(); + const interRay = new Vector3(); + const step = this.isLineSegments ? 2 : 1; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + if ( geometry.isBufferGeometry ) { - this.roughnessMap = source.roughnessMap; + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - this.metalnessMap = source.metalnessMap; + if ( index !== null ) { - this.alphaMap = source.alphaMap; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; + for ( let i = start, l = end - 1; i < l; i += step ) { - this.refractionRatio = source.refractionRatio; + const a = index.getX( i ); + const b = index.getX( i + 1 ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + vStart.fromBufferAttribute( positionAttribute, a ); + vEnd.fromBufferAttribute( positionAttribute, b ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - this.flatShading = source.flatShading; + if ( distSq > localThresholdSq ) continue; - this.vertexTangents = source.vertexTangents; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - return this; + const distance = raycaster.ray.origin.distanceTo( interRay ); - } + if ( distance < raycaster.near || distance > raycaster.far ) continue; -} + intersects.push( { -MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this -/** - * parameters = { - * clearcoat: , - * clearcoatMap: new THREE.Texture( ), - * clearcoatRoughness: , - * clearcoatRoughnessMap: new THREE.Texture( ), - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * - * reflectivity: , - * ior: , - * - * sheen: , - * - * transmission: , - * transmissionMap: new THREE.Texture( ) - * } - */ + } ); -class MeshPhysicalMaterial extends MeshStandardMaterial { + } - constructor( parameters ) { + } else { - super(); + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - this.defines = { + for ( let i = start, l = end - 1; i < l; i += step ) { - 'STANDARD': '', - 'PHYSICAL': '' + vStart.fromBufferAttribute( positionAttribute, i ); + vEnd.fromBufferAttribute( positionAttribute, i + 1 ); - }; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - this.type = 'MeshPhysicalMaterial'; + if ( distSq > localThresholdSq ) continue; - this.clearcoat = 0.0; - this.clearcoatMap = null; - this.clearcoatRoughness = 0.0; - this.clearcoatRoughnessMap = null; - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - this.reflectivity = 0.5; // maps to F0 = 0.04 + const distance = raycaster.ray.origin.distanceTo( interRay ); - Object.defineProperty( this, 'ior', { - get: function () { + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { - return ( 1 + 0.4 * this.reflectivity ) / ( 1 - 0.4 * this.reflectivity ); + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - }, - set: function ( ior ) { + } ); - this.reflectivity = clamp( 2.5 * ( ior - 1 ) / ( ior + 1 ), 0, 1 ); + } } - } ); - this.sheen = null; // null will disable sheen bsdf + } else if ( geometry.isGeometry ) { - this.transmission = 0.0; - this.transmissionMap = null; + console.error( 'THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.setValues( parameters ); + } } - copy( source ) { + updateMorphTargets() { - super.copy( source ); + const geometry = this.geometry; - this.defines = { + if ( geometry.isBufferGeometry ) { - 'STANDARD': '', - 'PHYSICAL': '' + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - }; + if ( keys.length > 0 ) { - this.clearcoat = source.clearcoat; - this.clearcoatMap = source.clearcoatMap; - this.clearcoatRoughness = source.clearcoatRoughness; - this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - this.reflectivity = source.reflectivity; + if ( morphAttribute !== undefined ) { + + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + + const name = morphAttribute[ m ].name || String( m ); + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; + + } - if ( source.sheen ) { + } - this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); + } } else { - this.sheen = null; + const morphTargets = geometry.morphTargets; - } + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - this.transmission = source.transmission; - this.transmissionMap = source.transmissionMap; + console.error( 'THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - return this; + } + + } } } -MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; +Line.prototype.isLine = true; -/** - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.MultiplyOperation, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ +const _start = /*@__PURE__*/ new Vector3(); +const _end = /*@__PURE__*/ new Vector3(); -class MeshPhongMaterial extends Material { +class LineSegments extends Line { - constructor( parameters ) { + constructor( geometry, material ) { - super(); + super( geometry, material ); - this.type = 'MeshPhongMaterial'; + this.type = 'LineSegments'; - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; + } - this.map = null; + computeLineDistances() { - this.lightMap = null; - this.lightMapIntensity = 1.0; + const geometry = this.geometry; - this.aoMap = null; - this.aoMapIntensity = 1.0; + if ( geometry.isBufferGeometry ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + // we assume non-indexed geometry - this.bumpMap = null; - this.bumpScale = 1; + if ( geometry.index === null ) { - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + const positionAttribute = geometry.attributes.position; + const lineDistances = []; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - this.specularMap = null; + _start.fromBufferAttribute( positionAttribute, i ); + _end.fromBufferAttribute( positionAttribute, i + 1 ); - this.alphaMap = null; + lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; + lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + } - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } else { - this.flatShading = false; + console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - this.setValues( parameters ); + } - } + } else if ( geometry.isGeometry ) { - copy( source ) { + console.error( 'THREE.LineSegments.computeLineDistances() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - super.copy( source ); + } - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + return this; - this.map = source.map; + } - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; +} - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; +LineSegments.prototype.isLineSegments = true; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; +class LineLoop extends Line { - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + constructor( geometry, material ) { - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + super( geometry, material ); - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.type = 'LineLoop'; - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.flatShading = source.flatShading; - - return this; - - } + } } -MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - -/** - * parameters = { - * color: , - * - * map: new THREE.Texture( ), - * gradientMap: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ +LineLoop.prototype.isLineLoop = true; -class MeshToonMaterial extends Material { +class PointsMaterial extends Material { constructor( parameters ) { super(); - this.defines = { 'TOON': '' }; - - this.type = 'MeshToonMaterial'; + this.type = 'PointsMaterial'; this.color = new Color( 0xffffff ); this.map = null; - this.gradientMap = null; - - this.lightMap = null; - this.lightMapIntensity = 1.0; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; this.alphaMap = null; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + this.size = 1; + this.sizeAttenuation = true; this.setValues( parameters ); @@ -31102,39 +31198,11 @@ class MeshToonMaterial extends Material { this.color.copy( source.color ); this.map = source.map; - this.gradientMap = source.gradientMap; - - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; - - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; return this; @@ -31142,65 +31210,25 @@ class MeshToonMaterial extends Material { } -MeshToonMaterial.prototype.isMeshToonMaterial = true; +PointsMaterial.prototype.isPointsMaterial = true; -/** - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * flatShading: - * } - */ +const _inverseMatrix = /*@__PURE__*/ new Matrix4(); +const _ray = /*@__PURE__*/ new Ray(); +const _sphere = /*@__PURE__*/ new Sphere(); +const _position$2 = /*@__PURE__*/ new Vector3(); -class MeshNormalMaterial extends Material { +class Points extends Object3D { - constructor( parameters ) { + constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { super(); - this.type = 'MeshNormalMaterial'; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; - - this.wireframe = false; - this.wireframeLinewidth = 1; - - this.fog = false; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + this.type = 'Points'; - this.flatShading = false; + this.geometry = geometry; + this.material = material; - this.setValues( parameters ); + this.updateMorphTargets(); } @@ -31208,4518 +31236,4421 @@ class MeshNormalMaterial extends Material { super.copy( source ); - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); - - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - this.flatShading = source.flatShading; + this.material = source.material; + this.geometry = source.geometry; return this; } -} - -MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - -/** - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ - -class MeshLambertMaterial extends Material { - - constructor( parameters ) { + raycast( raycaster, intersects ) { - super(); + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Points.threshold; + const drawRange = geometry.drawRange; - this.type = 'MeshLambertMaterial'; + // Checking boundingSphere distance to ray - this.color = new Color( 0xffffff ); // diffuse + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - this.map = null; + _sphere.copy( geometry.boundingSphere ); + _sphere.applyMatrix4( matrixWorld ); + _sphere.radius += threshold; - this.lightMap = null; - this.lightMapIntensity = 1.0; + if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - this.aoMap = null; - this.aoMapIntensity = 1.0; + // - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + _inverseMatrix.copy( matrixWorld ).invert(); + _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - this.specularMap = null; + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - this.alphaMap = null; + if ( geometry.isBufferGeometry ) { - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + if ( index !== null ) { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - this.setValues( parameters ); + for ( let i = start, il = end; i < il; i ++ ) { - } + const a = index.getX( i ); - copy( source ) { + _position$2.fromBufferAttribute( positionAttribute, a ); - super.copy( source ); + testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - this.color.copy( source.color ); + } - this.map = source.map; + } else { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + for ( let i = start, l = end; i < l; i ++ ) { - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + _position$2.fromBufferAttribute( positionAttribute, i ); - this.specularMap = source.specularMap; + testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + } - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } else { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + console.error( 'THREE.Points.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); - return this; + } } -} - -MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - -/** - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * - * flatShading: - * } - */ - -class MeshMatcapMaterial extends Material { - - constructor( parameters ) { - - super(); - - this.defines = { 'MATCAP': '' }; - - this.type = 'MeshMatcapMaterial'; - - this.color = new Color( 0xffffff ); // diffuse - - this.matcap = null; - - this.map = null; - - this.bumpMap = null; - this.bumpScale = 1; - - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); - - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + updateMorphTargets() { - this.alphaMap = null; + const geometry = this.geometry; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( geometry.isBufferGeometry ) { - this.flatShading = false; + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - this.setValues( parameters ); + if ( keys.length > 0 ) { - } + const morphAttribute = morphAttributes[ keys[ 0 ] ]; + if ( morphAttribute !== undefined ) { - copy( source ) { + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - super.copy( source ); + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - this.defines = { 'MATCAP': '' }; + const name = morphAttribute[ m ].name || String( m ); - this.color.copy( source.color ); + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - this.matcap = source.matcap; + } - this.map = source.map; + } - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + } else { - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + const morphTargets = geometry.morphTargets; - this.alphaMap = source.alphaMap; + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); - this.flatShading = source.flatShading; + } - return this; + } } } -MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; - -/** - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ - -class LineDashedMaterial extends LineBasicMaterial { +Points.prototype.isPoints = true; - constructor( parameters ) { +function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - super(); + const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - this.type = 'LineDashedMaterial'; + if ( rayPointDistanceSq < localThresholdSq ) { - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + const intersectPoint = new Vector3(); - this.setValues( parameters ); + _ray.closestPointToPoint( point, intersectPoint ); + intersectPoint.applyMatrix4( matrixWorld ); - } + const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - copy( source ) { + if ( distance < raycaster.near || distance > raycaster.far ) return; - super.copy( source ); + intersects.push( { - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint, + index: index, + face: null, + object: object - return this; + } ); } } -LineDashedMaterial.prototype.isLineDashedMaterial = true; - -const AnimationUtils = { +class VideoTexture extends Texture { - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { + constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - if ( AnimationUtils.isTypedArray( array ) ) { + super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); + this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - } + this.generateMipmaps = false; - return array.slice( from, to ); + const scope = this; - }, + function updateVideo() { - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { + scope.needsUpdate = true; + video.requestVideoFrameCallback( updateVideo ); - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + } - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + if ( 'requestVideoFrameCallback' in video ) { - return new type( array ); // create typed array + video.requestVideoFrameCallback( updateVideo ); } - return Array.prototype.slice.call( array ); // create Array + } - }, + clone() { - isTypedArray: function ( object ) { + return new this.constructor( this.image ).copy( this ); - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + } - }, + update() { - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { + const video = this.image; + const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - function compareTime( i, j ) { + if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - return times[ i ] - times[ j ]; + this.needsUpdate = true; } - const n = times.length; - const result = new Array( n ); - for ( let i = 0; i !== n; ++ i ) result[ i ] = i; + } - result.sort( compareTime ); +} - return result; +VideoTexture.prototype.isVideoTexture = true; - }, +class FramebufferTexture extends Texture { - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { + constructor( width, height, format ) { - const nValues = values.length; - const result = new values.constructor( nValues ); + super( { width, height } ); - for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + this.format = format; - const srcOffset = order[ i ] * stride; + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - for ( let j = 0; j !== stride; ++ j ) { + this.generateMipmaps = false; - result[ dstOffset ++ ] = values[ srcOffset + j ]; + this.needsUpdate = true; - } + } - } +} - return result; +FramebufferTexture.prototype.isFramebufferTexture = true; - }, +class CompressedTexture extends Texture { - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { + constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - let i = 1, key = jsonKeys[ 0 ]; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - key = jsonKeys[ i ++ ]; + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - } + this.flipY = false; - if ( key === undefined ) return; // no data + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - let value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data + this.generateMipmaps = false; - if ( Array.isArray( value ) ) { + } - do { +} - value = key[ valuePropertyName ]; +CompressedTexture.prototype.isCompressedTexture = true; - if ( value !== undefined ) { +class CanvasTexture extends Texture { - times.push( key.time ); - values.push.apply( values, value ); // push all elements + constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - } + super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - key = jsonKeys[ i ++ ]; + this.needsUpdate = true; - } while ( key !== undefined ); + } - } else if ( value.toArray !== undefined ) { +} - // ...assume THREE.Math-ish - - do { - - value = key[ valuePropertyName ]; - - if ( value !== undefined ) { - - times.push( key.time ); - value.toArray( values, values.length ); +CanvasTexture.prototype.isCanvasTexture = true; - } +/** + * Extensible curve object. + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following curves inherit from THREE.Curve: + * + * -- 2D curves -- + * THREE.ArcCurve + * THREE.CubicBezierCurve + * THREE.EllipseCurve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.SplineCurve + * + * -- 3D curves -- + * THREE.CatmullRomCurve3 + * THREE.CubicBezierCurve3 + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * + * A series of curves can be represented as a THREE.CurvePath. + * + **/ - key = jsonKeys[ i ++ ]; +class Curve { - } while ( key !== undefined ); + constructor() { - } else { + this.type = 'Curve'; - // otherwise push as-is + this.arcLengthDivisions = 200; - do { + } - value = key[ valuePropertyName ]; + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] - if ( value !== undefined ) { + getPoint( /* t, optionalTarget */ ) { - times.push( key.time ); - values.push( value ); + console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + return null; - } + } - key = jsonKeys[ i ++ ]; + // Get point at relative position in curve according to arc length + // - u [0 .. 1] - } while ( key !== undefined ); + getPointAt( u, optionalTarget ) { - } + const t = this.getUtoTmapping( u ); + return this.getPoint( t, optionalTarget ); - }, + } - subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { + // Get sequence of points using getPoint( t ) - const clip = sourceClip.clone(); + getPoints( divisions = 5 ) { - clip.name = name; + const points = []; - const tracks = []; + for ( let d = 0; d <= divisions; d ++ ) { - for ( let i = 0; i < clip.tracks.length; ++ i ) { + points.push( this.getPoint( d / divisions ) ); - const track = clip.tracks[ i ]; - const valueSize = track.getValueSize(); + } - const times = []; - const values = []; + return points; - for ( let j = 0; j < track.times.length; ++ j ) { + } - const frame = track.times[ j ] * fps; + // Get sequence of points using getPointAt( u ) - if ( frame < startFrame || frame >= endFrame ) continue; + getSpacedPoints( divisions = 5 ) { - times.push( track.times[ j ] ); + const points = []; - for ( let k = 0; k < valueSize; ++ k ) { + for ( let d = 0; d <= divisions; d ++ ) { - values.push( track.values[ j * valueSize + k ] ); + points.push( this.getPointAt( d / divisions ) ); - } + } - } + return points; - if ( times.length === 0 ) continue; + } - track.times = AnimationUtils.convertArray( times, track.times.constructor ); - track.values = AnimationUtils.convertArray( values, track.values.constructor ); + // Get total curve arc length - tracks.push( track ); + getLength() { - } + const lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - clip.tracks = tracks; + } - // find minimum .times value across all tracks in the trimmed clip + // Get list of cumulative segment lengths - let minStartTime = Infinity; + getLengths( divisions = this.arcLengthDivisions ) { - for ( let i = 0; i < clip.tracks.length; ++ i ) { + if ( this.cacheArcLengths && + ( this.cacheArcLengths.length === divisions + 1 ) && + ! this.needsUpdate ) { - if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { + return this.cacheArcLengths; - minStartTime = clip.tracks[ i ].times[ 0 ]; + } - } + this.needsUpdate = false; - } + const cache = []; + let current, last = this.getPoint( 0 ); + let sum = 0; - // shift all tracks such that clip begins at t=0 + cache.push( 0 ); - for ( let i = 0; i < clip.tracks.length; ++ i ) { + for ( let p = 1; p <= divisions; p ++ ) { - clip.tracks[ i ].shift( - 1 * minStartTime ); + current = this.getPoint( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; } - clip.resetDuration(); - - return clip; + this.cacheArcLengths = cache; - }, + return cache; // { sums: cache, sum: sum }; Sum is in the last element. - makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { + } - if ( fps <= 0 ) fps = 30; + updateArcLengths() { - const numTracks = referenceClip.tracks.length; - const referenceTime = referenceFrame / fps; + this.needsUpdate = true; + this.getLengths(); - // Make each track's values relative to the values at the reference frame - for ( let i = 0; i < numTracks; ++ i ) { + } - const referenceTrack = referenceClip.tracks[ i ]; - const referenceTrackType = referenceTrack.ValueTypeName; + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - // Skip this track if it's non-numeric - if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; + getUtoTmapping( u, distance ) { - // Find the track in the target clip whose name and type matches the reference track - const targetTrack = targetClip.tracks.find( function ( track ) { + const arcLengths = this.getLengths(); - return track.name === referenceTrack.name - && track.ValueTypeName === referenceTrackType; + let i = 0; + const il = arcLengths.length; - } ); + let targetArcLength; // The targeted u distance value to get - if ( targetTrack === undefined ) continue; + if ( distance ) { - let referenceOffset = 0; - const referenceValueSize = referenceTrack.getValueSize(); + targetArcLength = distance; - if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + } else { - referenceOffset = referenceValueSize / 3; + targetArcLength = u * arcLengths[ il - 1 ]; - } + } - let targetOffset = 0; - const targetValueSize = targetTrack.getValueSize(); + // binary search for the index with largest value smaller than target u distance - if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { + let low = 0, high = il - 1, comparison; - targetOffset = targetValueSize / 3; + while ( low <= high ) { - } + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - const lastIndex = referenceTrack.times.length - 1; - let referenceValue; + comparison = arcLengths[ i ] - targetArcLength; - // Find the value to subtract out of the track - if ( referenceTime <= referenceTrack.times[ 0 ] ) { + if ( comparison < 0 ) { - // Reference frame is earlier than the first keyframe, so just use the first keyframe - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + low = i + 1; - } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { + } else if ( comparison > 0 ) { - // Reference frame is after the last keyframe, so just use the last keyframe - const startIndex = lastIndex * referenceValueSize + referenceOffset; - const endIndex = startIndex + referenceValueSize - referenceOffset; - referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); + high = i - 1; } else { - // Interpolate to the reference value - const interpolant = referenceTrack.createInterpolant(); - const startIndex = referenceOffset; - const endIndex = referenceValueSize - referenceOffset; - interpolant.evaluate( referenceTime ); - referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - - } - - // Conjugate the quaternion - if ( referenceTrackType === 'quaternion' ) { + high = i; + break; - const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); - referenceQuat.toArray( referenceValue ); + // DONE } - // Subtract the reference value from all of the track values + } - const numTimes = targetTrack.times.length; - for ( let j = 0; j < numTimes; ++ j ) { + i = high; - const valueStart = j * targetValueSize + targetOffset; + if ( arcLengths[ i ] === targetArcLength ) { - if ( referenceTrackType === 'quaternion' ) { + return i / ( il - 1 ); - // Multiply the conjugate for quaternion track types - Quaternion.multiplyQuaternionsFlat( - targetTrack.values, - valueStart, - referenceValue, - 0, - targetTrack.values, - valueStart - ); + } - } else { + // we could get finer grain at lengths, or use simple interpolation between two points - const valueEnd = targetValueSize - targetOffset * 2; + const lengthBefore = arcLengths[ i ]; + const lengthAfter = arcLengths[ i + 1 ]; - // Subtract each value for all other numeric track types - for ( let k = 0; k < valueEnd; ++ k ) { + const segmentLength = lengthAfter - lengthBefore; - targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; + // determine where we are between the 'before' and 'after' points - } + const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - } + // add that fractional amount to t - } + const t = ( i + segmentFraction ) / ( il - 1 ); - } + return t; - targetClip.blendMode = AdditiveAnimationBlendMode; + } - return targetClip; + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation - } + getTangent( t, optionalTarget ) { -}; + const delta = 0.0001; + let t1 = t - delta; + let t2 = t + delta; -/** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - */ + // Capping in case of danger -class Interpolant { + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + const pt1 = this.getPoint( t1 ); + const pt2 = this.getPoint( t2 ); - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; + const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + tangent.copy( pt2 ).sub( pt1 ).normalize(); - this.settings = null; - this.DefaultSettings_ = {}; + return tangent; } - evaluate( t ) { + getTangentAt( u, optionalTarget ) { - const pp = this.parameterPositions; - let i1 = this._cachedIndex, - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + const t = this.getUtoTmapping( u ); + return this.getTangent( t, optionalTarget ); - validate_interval: { + } - seek: { + computeFrenetFrames( segments, closed ) { - let right; + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - linear_scan: { + const normal = new Vector3(); - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + const tangents = []; + const normals = []; + const binormals = []; - for ( let giveUpAt = i1 + 2; ; ) { + const vec = new Vector3(); + const mat = new Matrix4(); - if ( t1 === undefined ) { + // compute the tangent vectors for each segment on the curve - if ( t < t0 ) break forward_scan; + for ( let i = 0; i <= segments; i ++ ) { - // after end + const u = i / segments; - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + tangents[ i ] = this.getTangentAt( u, new Vector3() ); - } + } - if ( i1 === giveUpAt ) break; // this loop + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component - t0 = t1; - t1 = pp[ ++ i1 ]; + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + let min = Number.MAX_VALUE; + const tx = Math.abs( tangents[ 0 ].x ); + const ty = Math.abs( tangents[ 0 ].y ); + const tz = Math.abs( tangents[ 0 ].z ); - if ( t < t1 ) { + if ( tx <= min ) { - // we have arrived at the sought interval - break seek; + min = tx; + normal.set( 1, 0, 0 ); - } + } - } + if ( ty <= min ) { - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + min = ty; + normal.set( 0, 1, 0 ); - } + } - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + if ( tz <= min ) { - // looping? + normal.set( 0, 0, 1 ); - const t1global = pp[ 1 ]; + } - if ( t < t1global ) { + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - } - // linear reverse scan + // compute the slowly-varying normal and binormal vectors for each segment on the curve - for ( let giveUpAt = i1 - 2; ; ) { + for ( let i = 1; i <= segments; i ++ ) { - if ( t0 === undefined ) { + normals[ i ] = normals[ i - 1 ].clone(); - // before start + binormals[ i ] = binormals[ i - 1 ].clone(); - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - } + if ( vec.length() > Number.EPSILON ) { - if ( i1 === giveUpAt ) break; // this loop + vec.normalize(); - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - if ( t >= t0 ) { + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - // we have arrived at the sought interval - break seek; + } - } + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - } + } - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - } + if ( closed === true ) { - // the interval is valid + let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); + theta /= segments; - break validate_interval; + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - } // linear scan + theta = - theta; - // binary search + } - while ( i1 < right ) { + for ( let i = 1; i <= segments; i ++ ) { - const mid = ( i1 + right ) >>> 1; + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - if ( t < pp[ mid ] ) { + } - right = mid; + } - } else { + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; - i1 = mid + 1; + } - } + clone() { - } + return new this.constructor().copy( this ); - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + } - // check boundary cases, again + copy( source ) { - if ( t0 === undefined ) { + this.arcLengthDivisions = source.arcLengthDivisions; - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + return this; - } + } - if ( t1 === undefined ) { + toJSON() { - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + const data = { + metadata: { + version: 4.5, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; - } + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; - } // seek + return data; - this._cachedIndex = i1; + } - this.intervalChanged_( i1, t0, t1 ); + fromJSON( json ) { - } // validate_interval + this.arcLengthDivisions = json.arcLengthDivisions; - return this.interpolate_( i1, t0, t, t1 ); + return this; } - getSettings_() { +} - return this.settings || this.DefaultSettings_; +class EllipseCurve extends Curve { - } + constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { - copySampleValue_( index ) { + super(); - // copies a sample value to the result buffer + this.type = 'EllipseCurve'; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + this.aX = aX; + this.aY = aY; - for ( let i = 0; i !== stride; ++ i ) { + this.xRadius = xRadius; + this.yRadius = yRadius; - result[ i ] = values[ offset + i ]; + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - } + this.aClockwise = aClockwise; - return result; + this.aRotation = aRotation; } - // Template methods for derived classes: - - interpolate_( /* i1, t0, t, t1 */ ) { + getPoint( t, optionalTarget ) { - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer + const point = optionalTarget || new Vector2(); - } + const twoPi = Math.PI * 2; + let deltaAngle = this.aEndAngle - this.aStartAngle; + const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - intervalChanged_( /* i1, t0, t1 */ ) { + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - // empty + if ( deltaAngle < Number.EPSILON ) { - } + if ( samePoints ) { -} + deltaAngle = 0; -// ALIAS DEFINITIONS + } else { -Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; -Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; + deltaAngle = twoPi; -/** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - */ + } -class CubicInterpolant extends Interpolant { + } - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + if ( this.aClockwise === true && ! samePoints ) { - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + if ( deltaAngle === twoPi ) { - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; + deltaAngle = - twoPi; - this.DefaultSettings_ = { + } else { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + deltaAngle = deltaAngle - twoPi; - }; + } - } + } - intervalChanged_( i1, t0, t1 ) { + const angle = this.aStartAngle + t * deltaAngle; + let x = this.aX + this.xRadius * Math.cos( angle ); + let y = this.aY + this.yRadius * Math.sin( angle ); - const pp = this.parameterPositions; - let iPrev = i1 - 2, - iNext = i1 + 1, + if ( this.aRotation !== 0 ) { - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; + const cos = Math.cos( this.aRotation ); + const sin = Math.sin( this.aRotation ); - if ( tPrev === undefined ) { + const tx = x - this.aX; + const ty = y - this.aY; - switch ( this.getSettings_().endingStart ) { + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; - case ZeroSlopeEnding: + } - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + return point.set( x, y ); - break; + } - case WrapAroundEnding: + copy( source ) { - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + super.copy( source ); - break; + this.aX = source.aX; + this.aY = source.aY; - default: // ZeroCurvatureEnding + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; - } + this.aClockwise = source.aClockwise; - } + this.aRotation = source.aRotation; - if ( tNext === undefined ) { + return this; - switch ( this.getSettings_().endingEnd ) { + } - case ZeroSlopeEnding: + toJSON() { - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; + const data = super.toJSON(); - break; + data.aX = this.aX; + data.aY = this.aY; - case WrapAroundEnding: + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; - break; + data.aClockwise = this.aClockwise; - default: // ZeroCurvatureEnding + data.aRotation = this.aRotation; - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + return data; - } + } - } + fromJSON( json ) { - const halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; + super.fromJSON( json ); - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; + this.aX = json.aX; + this.aY = json.aY; - } + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; - interpolate_( i1, t0, t, t1 ) { + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + this.aClockwise = json.aClockwise; - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, + this.aRotation = json.aRotation; - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; + return this; - // evaluate polynomials + } - const sP = - wP * ppp + 2 * wP * pp - wP * p; - const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - const sN = wN * ppp - wN * pp; +} - // combine data linearly +EllipseCurve.prototype.isEllipseCurve = true; - for ( let i = 0; i !== stride; ++ i ) { +class ArcCurve extends EllipseCurve { - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - } + super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - return result; + this.type = 'ArcCurve'; } } -class LinearInterpolant extends Interpolant { +ArcCurve.prototype.isArcCurve = true; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { +/** + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - } +/* +Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM - interpolate_( i1, t0, t, t1 ) { +This CubicPoly class could be used for reusing some variables and calculations, +but for three.js curve use, it could be possible inlined and flatten into a single function call +which can be placed in CurveUtils. +*/ - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, +function CubicPoly() { - offset1 = i1 * stride, - offset0 = offset1 - stride, + let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + function init( x0, x1, t0, t1 ) { - for ( let i = 0; i !== stride; ++ i ) { + c0 = x0; + c1 = t0; + c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + } - } + return { - return result; + initCatmullRom: function ( x0, x1, x2, x3, tension ) { - } + init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); -} + }, -/** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - */ + initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { -class DiscreteInterpolant extends Interpolant { + // compute tangents when parameterized in [t1,t2] + let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + init( x1, x2, t1, t2 ); - } + }, - interpolate_( i1 /*, t0, t, t1 */ ) { + calc: function ( t ) { - return this.copySampleValue_( i1 - 1 ); + const t2 = t * t; + const t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; - } + } + + }; } -class KeyframeTrack { +// - constructor( name, times, values, interpolation ) { +const tmp = new Vector3(); +const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); +class CatmullRomCurve3 extends Curve { - this.name = name; + constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); + super(); - this.setInterpolation( interpolation || this.DefaultInterpolation ); + this.type = 'CatmullRomCurve3'; + + this.points = points; + this.closed = closed; + this.curveType = curveType; + this.tension = tension; } - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + getPoint( t, optionalTarget = new Vector3() ) { - static toJSON( track ) { + const point = optionalTarget; - const trackType = track.constructor; + const points = this.points; + const l = points.length; - let json; + const p = ( l - ( this.closed ? 0 : 1 ) ) * t; + let intPoint = Math.floor( p ); + let weight = p - intPoint; - // derived classes can define a static toJSON method - if ( trackType.toJSON !== this.toJSON ) { + if ( this.closed ) { - json = trackType.toJSON( track ); + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - } else { + } else if ( weight === 0 && intPoint === l - 1 ) { - // by default, we assume the data can be serialized as-is - json = { + intPoint = l - 2; + weight = 1; - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) + } - }; + let p0, p3; // 4 points (p1 & p2 defined below) - const interpolation = track.getInterpolation(); + if ( this.closed || intPoint > 0 ) { - if ( interpolation !== track.DefaultInterpolation ) { + p0 = points[ ( intPoint - 1 ) % l ]; - json.interpolation = interpolation; + } else { - } + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; } - json.type = track.ValueTypeName; // mandatory - - return json; - - } + const p1 = points[ intPoint % l ]; + const p2 = points[ ( intPoint + 1 ) % l ]; - InterpolantFactoryMethodDiscrete( result ) { + if ( this.closed || intPoint + 2 < l ) { - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); + p3 = points[ ( intPoint + 2 ) % l ]; - } + } else { - InterpolantFactoryMethodLinear( result ) { + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); + } - } + if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - InterpolantFactoryMethodSmooth( result ) { + // init Centripetal / Chordal Catmull-Rom + const pow = this.curveType === 'chordal' ? 0.5 : 0.25; + let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; - } + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - setInterpolation( interpolation ) { + } else if ( this.curveType === 'catmullrom' ) { - let factoryMethod; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - switch ( interpolation ) { + } - case InterpolateDiscrete: + point.set( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - factoryMethod = this.InterpolantFactoryMethodDiscrete; + return point; - break; + } - case InterpolateLinear: + copy( source ) { - factoryMethod = this.InterpolantFactoryMethodLinear; + super.copy( source ); - break; + this.points = []; - case InterpolateSmooth: + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - factoryMethod = this.InterpolantFactoryMethodSmooth; + const point = source.points[ i ]; - break; + this.points.push( point.clone() ); } - if ( factoryMethod === undefined ) { - - const message = 'unsupported interpolation for ' + - this.ValueTypeName + ' keyframe track named ' + this.name; - - if ( this.createInterpolant === undefined ) { + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { + return this; - this.setInterpolation( this.DefaultInterpolation ); + } - } else { + toJSON() { - throw new Error( message ); // fatal, in this case + const data = super.toJSON(); - } + data.points = []; - } + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - console.warn( 'THREE.KeyframeTrack:', message ); - return this; + const point = this.points[ i ]; + data.points.push( point.toArray() ); } - this.createInterpolant = factoryMethod; + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; - return this; + return data; } - getInterpolation() { - - switch ( this.createInterpolant ) { + fromJSON( json ) { - case this.InterpolantFactoryMethodDiscrete: + super.fromJSON( json ); - return InterpolateDiscrete; + this.points = []; - case this.InterpolantFactoryMethodLinear: + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - return InterpolateLinear; + const point = json.points[ i ]; + this.points.push( new Vector3().fromArray( point ) ); - case this.InterpolantFactoryMethodSmooth: + } - return InterpolateSmooth; + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; - } + return this; } - getValueSize() { +} - return this.values.length / this.times.length; +CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - } +/** + * Bezier Curves formulas obtained from + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + */ - // move all keyframes either forwards or backwards in time - shift( timeOffset ) { +function CatmullRom( t, p0, p1, p2, p3 ) { - if ( timeOffset !== 0.0 ) { + const v0 = ( p2 - p0 ) * 0.5; + const v1 = ( p3 - p1 ) * 0.5; + const t2 = t * t; + const t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - const times = this.times; +} - for ( let i = 0, n = times.length; i !== n; ++ i ) { +// - times[ i ] += timeOffset; +function QuadraticBezierP0( t, p ) { - } + const k = 1 - t; + return k * k * p; - } +} - return this; +function QuadraticBezierP1( t, p ) { - } + return 2 * ( 1 - t ) * t * p; - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale( timeScale ) { +} - if ( timeScale !== 1.0 ) { +function QuadraticBezierP2( t, p ) { - const times = this.times; + return t * t * p; - for ( let i = 0, n = times.length; i !== n; ++ i ) { +} - times[ i ] *= timeScale; +function QuadraticBezier( t, p0, p1, p2 ) { - } + return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + + QuadraticBezierP2( t, p2 ); - } +} - return this; +// - } +function CubicBezierP0( t, p ) { - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim( startTime, endTime ) { - - const times = this.times, - nKeys = times.length; - - let from = 0, - to = nKeys - 1; + const k = 1 - t; + return k * k * k * p; - while ( from !== nKeys && times[ from ] < startTime ) { +} - ++ from; +function CubicBezierP1( t, p ) { - } + const k = 1 - t; + return 3 * k * k * t * p; - while ( to !== - 1 && times[ to ] > endTime ) { +} - -- to; +function CubicBezierP2( t, p ) { - } + return 3 * ( 1 - t ) * t * t * p; - ++ to; // inclusive -> exclusive bound +} - if ( from !== 0 || to !== nKeys ) { +function CubicBezierP3( t, p ) { - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) { + return t * t * t * p; - to = Math.max( to, 1 ); - from = to - 1; +} - } +function CubicBezier( t, p0, p1, p2, p3 ) { - const stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); + return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + + CubicBezierP3( t, p3 ); - } +} - return this; +class CubicBezierCurve extends Curve { - } + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate() { + super(); - let valid = true; + this.type = 'CubicBezierCurve'; - const valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; + } - } + getPoint( t, optionalTarget = new Vector2() ) { - const times = this.times, - values = this.values, + const point = optionalTarget; - nKeys = times.length; + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - if ( nKeys === 0 ) { + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) + ); - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; + return point; - } + } - let prevTime = null; + copy( source ) { - for ( let i = 0; i !== nKeys; i ++ ) { + super.copy( source ); - const currTime = times[ i ]; + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + return this; - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; + } - } + toJSON() { - if ( prevTime !== null && prevTime > currTime ) { + const data = super.toJSON(); - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - } + return data; - prevTime = currTime; + } - } + fromJSON( json ) { - if ( values !== undefined ) { + super.fromJSON( json ); - if ( AnimationUtils.isTypedArray( values ) ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - for ( let i = 0, n = values.length; i !== n; ++ i ) { + return this; - const value = values[ i ]; + } - if ( isNaN( value ) ) { +} - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; +CubicBezierCurve.prototype.isCubicBezierCurve = true; - } +class CubicBezierCurve3 extends Curve { - } + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { - } + super(); - } + this.type = 'CubicBezierCurve3'; - return valid; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; } - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize() { + getPoint( t, optionalTarget = new Vector3() ) { - // times or values may be shared with other tracks, so overwriting is unsafe - const times = AnimationUtils.arraySlice( this.times ), - values = AnimationUtils.arraySlice( this.values ), - stride = this.getValueSize(), + const point = optionalTarget; - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - lastIndex = times.length - 1; + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), + CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) + ); - let writeIndex = 1; + return point; - for ( let i = 1; i < lastIndex; ++ i ) { + } - let keep = false; + copy( source ) { - const time = times[ i ]; - const timeNext = times[ i + 1 ]; + super.copy( source ); - // remove adjacent keyframes scheduled at the same time + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { + return this; - if ( ! smoothInterpolation ) { + } - // remove unnecessary keyframes same as their neighbors + toJSON() { - const offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + const data = super.toJSON(); - for ( let j = 0; j !== stride; ++ j ) { + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - const value = values[ offset + j ]; + return data; - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + } - keep = true; - break; + fromJSON( json ) { - } + super.fromJSON( json ); - } + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - } else { + return this; - keep = true; + } - } +} - } +CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - // in-place compaction +class LineCurve extends Curve { - if ( keep ) { + constructor( v1 = new Vector2(), v2 = new Vector2() ) { - if ( i !== writeIndex ) { + super(); - times[ writeIndex ] = times[ i ]; + this.type = 'LineCurve'; - const readOffset = i * stride, - writeOffset = writeIndex * stride; + this.v1 = v1; + this.v2 = v2; - for ( let j = 0; j !== stride; ++ j ) { + } - values[ writeOffset + j ] = values[ readOffset + j ]; + getPoint( t, optionalTarget = new Vector2() ) { - } + const point = optionalTarget; - } + if ( t === 1 ) { - ++ writeIndex; + point.copy( this.v2 ); - } + } else { + + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); } - // flush last keyframe (compaction looks ahead) + return point; - if ( lastIndex > 0 ) { + } - times[ writeIndex ] = times[ lastIndex ]; + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { + return this.getPoint( u, optionalTarget ); - values[ writeOffset + j ] = values[ readOffset + j ]; + } - } + getTangent( t, optionalTarget ) { - ++ writeIndex; + const tangent = optionalTarget || new Vector2(); - } + tangent.copy( this.v2 ).sub( this.v1 ).normalize(); - if ( writeIndex !== times.length ) { + return tangent; - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + } - } else { + copy( source ) { - this.times = times; - this.values = values; + super.copy( source ); - } + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); return this; } - clone() { - - const times = AnimationUtils.arraySlice( this.times, 0 ); - const values = AnimationUtils.arraySlice( this.values, 0 ); + toJSON() { - const TypedKeyframeTrack = this.constructor; - const track = new TypedKeyframeTrack( this.name, times, values ); + const data = super.toJSON(); - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - return track; + return data; } -} + fromJSON( json ) { -KeyframeTrack.prototype.TimeBufferType = Float32Array; -KeyframeTrack.prototype.ValueBufferType = Float32Array; -KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; + super.fromJSON( json ); -/** - * A Track of Boolean keyframe values. - */ -class BooleanKeyframeTrack extends KeyframeTrack {} + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); -BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; -BooleanKeyframeTrack.prototype.ValueBufferType = Array; -BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; -BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; -BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + return this; -/** - * A Track of keyframe values that represent color. - */ -class ColorKeyframeTrack extends KeyframeTrack {} + } -ColorKeyframeTrack.prototype.ValueTypeName = 'color'; +} -/** - * A Track of numeric keyframe values. - */ -class NumberKeyframeTrack extends KeyframeTrack {} +LineCurve.prototype.isLineCurve = true; -NumberKeyframeTrack.prototype.ValueTypeName = 'number'; +class LineCurve3 extends Curve { -/** - * Spherical linear unit quaternion interpolant. - */ + constructor( v1 = new Vector3(), v2 = new Vector3() ) { -class QuaternionLinearInterpolant extends Interpolant { + super(); - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + this.type = 'LineCurve3'; + this.isLineCurve3 = true; - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + this.v1 = v1; + this.v2 = v2; } + getPoint( t, optionalTarget = new Vector3() ) { - interpolate_( i1, t0, t, t1 ) { - - const result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + const point = optionalTarget; - alpha = ( t - t0 ) / ( t1 - t0 ); + if ( t === 1 ) { - let offset = i1 * stride; + point.copy( this.v2 ); - for ( let end = offset + stride; offset !== end; offset += 4 ) { + } else { - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); } - return result; + return point; } + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { -} + return this.getPoint( u, optionalTarget ); -/** - * A Track of quaternion keyframe values. - */ -class QuaternionKeyframeTrack extends KeyframeTrack { + } + copy( source ) { - InterpolantFactoryMethodLinear( result ) { + super.copy( source ); - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + + return this; } + toJSON() { -} + const data = super.toJSON(); -QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; -// ValueBufferType is inherited -QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; -QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); -/** - * A Track that interpolates Strings - */ -class StringKeyframeTrack extends KeyframeTrack {} + return data; -StringKeyframeTrack.prototype.ValueTypeName = 'string'; -StringKeyframeTrack.prototype.ValueBufferType = Array; -StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; -StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; -StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; + } + fromJSON( json ) { -/** - * A Track of vectored keyframe values. - */ -class VectorKeyframeTrack extends KeyframeTrack {} + super.fromJSON( json ); -VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); -class AnimationClip { + return this; - constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { + } - this.name = name; - this.tracks = tracks; - this.duration = duration; - this.blendMode = blendMode; +} - this.uuid = generateUUID(); +class QuadraticBezierCurve extends Curve { - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { - this.resetDuration(); + super(); - } + this.type = 'QuadraticBezierCurve'; + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; } + getPoint( t, optionalTarget = new Vector2() ) { - static parse( json ) { + const point = optionalTarget; - const tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ) + ); - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); + return point; - } + } - const clip = new this( json.name, json.duration, tracks, json.blendMode ); - clip.uuid = json.uuid; + copy( source ) { - return clip; + super.copy( source ); + + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + + return this; } - static toJSON( clip ) { + toJSON() { - const tracks = [], - clipTracks = clip.tracks; + const data = super.toJSON(); - const json = { + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid, - 'blendMode': clip.blendMode + return data; - }; + } - for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { + fromJSON( json ) { - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + super.fromJSON( json ); - } + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - return json; + return this; } - static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { +} - const numMorphTargets = morphTargetSequence.length; - const tracks = []; +QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - for ( let i = 0; i < numMorphTargets; i ++ ) { +class QuadraticBezierCurve3 extends Curve { - let times = []; - let values = []; + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + super(); - values.push( 0, 1, 0 ); + this.type = 'QuadraticBezierCurve3'; - const order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { + } - times.push( numMorphTargets ); - values.push( values[ 0 ] ); + getPoint( t, optionalTarget = new Vector3() ) { - } + const point = optionalTarget; - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - } + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ), + QuadraticBezier( t, v0.z, v1.z, v2.z ) + ); - return new this( name, - 1, tracks ); + return point; } - static findByName( objectOrClipArray, name ) { + copy( source ) { - let clipArray = objectOrClipArray; - - if ( ! Array.isArray( objectOrClipArray ) ) { - - const o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + super.copy( source ); - } + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - for ( let i = 0; i < clipArray.length; i ++ ) { + return this; - if ( clipArray[ i ].name === name ) { + } - return clipArray[ i ]; + toJSON() { - } + const data = super.toJSON(); - } + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - return null; + return data; } - static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - - const animationToMorphTargets = {}; + fromJSON( json ) { - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - const pattern = /^([\w-]*?)([\d]+)$/; + super.fromJSON( json ); - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - const morphTarget = morphTargets[ i ]; - const parts = morphTarget.name.match( pattern ); + return this; - if ( parts && parts.length > 1 ) { + } - const name = parts[ 1 ]; +} - let animationMorphTargets = animationToMorphTargets[ name ]; +QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - if ( ! animationMorphTargets ) { +class SplineCurve extends Curve { - animationToMorphTargets[ name ] = animationMorphTargets = []; + constructor( points = [] ) { - } + super(); - animationMorphTargets.push( morphTarget ); + this.type = 'SplineCurve'; - } + this.points = points; - } + } - const clips = []; + getPoint( t, optionalTarget = new Vector2() ) { - for ( const name in animationToMorphTargets ) { + const point = optionalTarget; - clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + const points = this.points; + const p = ( points.length - 1 ) * t; - } + const intPoint = Math.floor( p ); + const weight = p - intPoint; - return clips; + const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + const p1 = points[ intPoint ]; + const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - } + point.set( + CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), + CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) + ); - // parse the animation.hierarchy format - static parseAnimation( animation, bones ) { + return point; - if ( ! animation ) { + } - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; + copy( source ) { - } + super.copy( source ); - const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { + this.points = []; - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - const times = []; - const values = []; + const point = source.points[ i ]; - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); + this.points.push( point.clone() ); - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + } - destTracks.push( new trackType( trackName, times, values ) ); + return this; - } + } - } + toJSON() { - }; + const data = super.toJSON(); - const tracks = []; + data.points = []; - const clipName = animation.name || 'default'; - const fps = animation.fps || 30; - const blendMode = animation.blendMode; + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - // automatic length determination in AnimationClip. - let duration = animation.length || - 1; + const point = this.points[ i ]; + data.points.push( point.toArray() ); - const hierarchyTracks = animation.hierarchy || []; + } - for ( let h = 0; h < hierarchyTracks.length; h ++ ) { + return data; - const animationKeys = hierarchyTracks[ h ].keys; + } - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; + fromJSON( json ) { - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { + super.fromJSON( json ); - // figure out all morph targets used in this track - const morphTargetNames = {}; + this.points = []; - let k; + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - for ( k = 0; k < animationKeys.length; k ++ ) { + const point = json.points[ i ]; + this.points.push( new Vector2().fromArray( point ) ); - if ( animationKeys[ k ].morphTargets ) { + } - for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { + return this; - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; + } - } +} - } +SplineCurve.prototype.isSplineCurve = true; - } +var Curves = /*#__PURE__*/Object.freeze({ + __proto__: null, + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve +}); - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( const morphTargetName in morphTargetNames ) { +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ - const times = []; - const values = []; +class CurvePath extends Curve { - for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { + constructor() { - const animationKey = animationKeys[ k ]; + super(); - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); + this.type = 'CurvePath'; - } + this.curves = []; + this.autoClose = false; // Automatically closes the path - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); + } - } + add( curve ) { - duration = morphTargetNames.length * ( fps || 1.0 ); + this.curves.push( curve ); - } else { + } - // ...assume skeletal animation + closePath() { - const boneName = '.bones[' + bones[ h ].name + ']'; + // Add a line curve if start and end of lines are not connected + const startPoint = this.curves[ 0 ].getPoint( 0 ); + const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); + if ( ! startPoint.equals( endPoint ) ) { - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); + this.curves.push( new LineCurve( endPoint, startPoint ) ); - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); + } - } + } - } + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: - if ( tracks.length === 0 ) { + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') - return null; + getPoint( t, optionalTarget ) { - } + const d = t * this.getLength(); + const curveLengths = this.getCurveLengths(); + let i = 0; - const clip = new this( clipName, duration, tracks, blendMode ); + // To think about boundaries points. - return clip; + while ( i < curveLengths.length ) { - } + if ( curveLengths[ i ] >= d ) { - resetDuration() { + const diff = curveLengths[ i ] - d; + const curve = this.curves[ i ]; - const tracks = this.tracks; - let duration = 0; + const segmentLength = curve.getLength(); + const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - for ( let i = 0, n = tracks.length; i !== n; ++ i ) { + return curve.getPointAt( u, optionalTarget ); - const track = this.tracks[ i ]; + } - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); + i ++; } - this.duration = duration; + return null; - return this; + // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - return BooleanKeyframeTrack; + points.push( points[ 0 ] ); - case 'string': + } - return StringKeyframeTrack; + return points; } - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - -} - -function parseKeyframeTrack( json ) { + copy( source ) { - if ( json.type === undefined ) { + super.copy( source ); - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); + this.curves = []; - } + for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - const trackType = getTrackTypeForValueTypeName( json.type ); + const curve = source.curves[ i ]; - if ( json.times === undefined ) { + this.curves.push( curve.clone() ); - const times = [], values = []; + } - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + this.autoClose = source.autoClose; - json.times = times; - json.values = values; + return this; } - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + toJSON() { - return trackType.parse( json ); + const data = super.toJSON(); - } else { + data.autoClose = this.autoClose; + data.curves = []; - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - } + const curve = this.curves[ i ]; + data.curves.push( curve.toJSON() ); -} + } -const Cache = { + return data; - enabled: false, + } - files: {}, + fromJSON( json ) { - add: function ( key, file ) { + super.fromJSON( json ); - if ( this.enabled === false ) return; + this.autoClose = json.autoClose; + this.curves = []; - // console.log( 'THREE.Cache', 'Adding key:', key ); + for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - this.files[ key ] = file; + const curve = json.curves[ i ]; + this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - }, + } - get: function ( key ) { + return this; - if ( this.enabled === false ) return; + } - // console.log( 'THREE.Cache', 'Checking key:', key ); +} - return this.files[ key ]; +class Path extends CurvePath { - }, + constructor( points ) { - remove: function ( key ) { + super(); + this.type = 'Path'; - delete this.files[ key ]; + this.currentPoint = new Vector2(); - }, + if ( points ) { - clear: function () { + this.setFromPoints( points ); - this.files = {}; + } } -}; + setFromPoints( points ) { -class LoadingManager { + this.moveTo( points[ 0 ].x, points[ 0 ].y ); - constructor( onLoad, onProgress, onError ) { + for ( let i = 1, l = points.length; i < l; i ++ ) { - const scope = this; + this.lineTo( points[ i ].x, points[ i ].y ); - let isLoading = false; - let itemsLoaded = 0; - let itemsTotal = 0; - let urlModifier = undefined; - const handlers = []; + } - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor + return this; - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + } - this.itemStart = function ( url ) { + moveTo( x, y ) { - itemsTotal ++; + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - if ( isLoading === false ) { + return this; - if ( scope.onStart !== undefined ) { + } - scope.onStart( url, itemsLoaded, itemsTotal ); + lineTo( x, y ) { - } + const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); + this.curves.push( curve ); - } + this.currentPoint.set( x, y ); - isLoading = true; + return this; - }; + } - this.itemEnd = function ( url ) { + quadraticCurveTo( aCPx, aCPy, aX, aY ) { - itemsLoaded ++; + const curve = new QuadraticBezierCurve( + this.currentPoint.clone(), + new Vector2( aCPx, aCPy ), + new Vector2( aX, aY ) + ); - if ( scope.onProgress !== undefined ) { + this.curves.push( curve ); - scope.onProgress( url, itemsLoaded, itemsTotal ); + this.currentPoint.set( aX, aY ); - } + return this; - if ( itemsLoaded === itemsTotal ) { + } - isLoading = false; + bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - if ( scope.onLoad !== undefined ) { + const curve = new CubicBezierCurve( + this.currentPoint.clone(), + new Vector2( aCP1x, aCP1y ), + new Vector2( aCP2x, aCP2y ), + new Vector2( aX, aY ) + ); - scope.onLoad(); + this.curves.push( curve ); - } + this.currentPoint.set( aX, aY ); - } + return this; - }; + } - this.itemError = function ( url ) { + splineThru( pts /*Array of Vector*/ ) { - if ( scope.onError !== undefined ) { + const npts = [ this.currentPoint.clone() ].concat( pts ); - scope.onError( url ); + const curve = new SplineCurve( npts ); + this.curves.push( curve ); - } + this.currentPoint.copy( pts[ pts.length - 1 ] ); - }; + return this; - this.resolveURL = function ( url ) { + } - if ( urlModifier ) { + arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - return urlModifier( url ); + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - } + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - return url; + return this; - }; + } - this.setURLModifier = function ( transform ) { + absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - urlModifier = transform; + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - return this; + return this; - }; + } - this.addHandler = function ( regex, loader ) { + ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - handlers.push( regex, loader ); + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - return this; + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - }; + return this; - this.removeHandler = function ( regex ) { + } - const index = handlers.indexOf( regex ); + absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - if ( index !== - 1 ) { + const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - handlers.splice( index, 2 ); + if ( this.curves.length > 0 ) { - } + // if a previous curve is present, attempt to join + const firstPoint = curve.getPoint( 0 ); - return this; + if ( ! firstPoint.equals( this.currentPoint ) ) { - }; + this.lineTo( firstPoint.x, firstPoint.y ); - this.getHandler = function ( file ) { + } - for ( let i = 0, l = handlers.length; i < l; i += 2 ) { + } - const regex = handlers[ i ]; - const loader = handlers[ i + 1 ]; + this.curves.push( curve ); - if ( regex.global ) regex.lastIndex = 0; // see #17920 + const lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); - if ( regex.test( file ) ) { + return this; - return loader; + } - } + copy( source ) { - } + super.copy( source ); - return null; + this.currentPoint.copy( source.currentPoint ); - }; + return this; } -} - -const DefaultLoadingManager = new LoadingManager(); - -class Loader { + toJSON() { - constructor( manager ) { + const data = super.toJSON(); - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + data.currentPoint = this.currentPoint.toArray(); - this.crossOrigin = 'anonymous'; - this.withCredentials = false; - this.path = ''; - this.resourcePath = ''; - this.requestHeader = {}; + return data; } - load( /* url, onLoad, onProgress, onError */ ) {} - - loadAsync( url, onProgress ) { - - const scope = this; + fromJSON( json ) { - return new Promise( function ( resolve, reject ) { + super.fromJSON( json ); - scope.load( url, resolve, onProgress, reject ); + this.currentPoint.fromArray( json.currentPoint ); - } ); + return this; } - parse( /* data */ ) {} - - setCrossOrigin( crossOrigin ) { +} - this.crossOrigin = crossOrigin; - return this; +new Vector3(); +new Vector3(); +new Vector3(); +new Triangle(); - } +class Shape extends Path { - setWithCredentials( value ) { + constructor( points ) { - this.withCredentials = value; - return this; + super( points ); - } + this.uuid = generateUUID(); - setPath( path ) { + this.type = 'Shape'; - this.path = path; - return this; + this.holes = []; } - setResourcePath( resourcePath ) { + getPointsHoles( divisions ) { - this.resourcePath = resourcePath; - return this; + const holesPts = []; - } + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { - setRequestHeader( requestHeader ) { + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - this.requestHeader = requestHeader; - return this; + } + + return holesPts; } -} + // get points of shape and holes (keypoints based on segments parameter) -const loading = {}; + extractPoints( divisions ) { -class FileLoader extends Loader { + return { - constructor( manager ) { + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) - super( manager ); + }; } - load( url, onLoad, onProgress, onError ) { + copy( source ) { - if ( url === undefined ) url = ''; + super.copy( source ); - if ( this.path !== undefined ) url = this.path + url; + this.holes = []; - url = this.manager.resolveURL( url ); + for ( let i = 0, l = source.holes.length; i < l; i ++ ) { - const scope = this; + const hole = source.holes[ i ]; - const cached = Cache.get( url ); + this.holes.push( hole.clone() ); - if ( cached !== undefined ) { + } - scope.manager.itemStart( url ); + return this; - setTimeout( function () { + } - if ( onLoad ) onLoad( cached ); + toJSON() { - scope.manager.itemEnd( url ); + const data = super.toJSON(); - }, 0 ); + data.uuid = this.uuid; + data.holes = []; - return cached; + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + + const hole = this.holes[ i ]; + data.holes.push( hole.toJSON() ); } - // Check if request is duplicate + return data; - if ( loading[ url ] !== undefined ) { + } - loading[ url ].push( { + fromJSON( json ) { - onLoad: onLoad, - onProgress: onProgress, - onError: onError + super.fromJSON( json ); - } ); + this.uuid = json.uuid; + this.holes = []; - return; + for ( let i = 0, l = json.holes.length; i < l; i ++ ) { + + const hole = json.holes[ i ]; + this.holes.push( new Path().fromJSON( hole ) ); } - // Check for data: URI - const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - const dataUriRegexResult = url.match( dataUriRegex ); - let request; + return this; - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { + } - const mimeType = dataUriRegexResult[ 1 ]; - const isBase64 = !! dataUriRegexResult[ 2 ]; +} - let data = dataUriRegexResult[ 3 ]; - data = decodeURIComponent( data ); +/** + * Port from https://github.com/mapbox/earcut (v2.2.2) + */ - if ( isBase64 ) data = atob( data ); +const Earcut = { - try { + triangulate: function ( data, holeIndices, dim = 2 ) { - let response; - const responseType = ( this.responseType || '' ).toLowerCase(); + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; + let outerNode = linkedList( data, 0, outerLen, dim, true ); + const triangles = []; - switch ( responseType ) { + if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; - case 'arraybuffer': - case 'blob': + let minX, minY, maxX, maxY, x, y, invSize; - const view = new Uint8Array( data.length ); + if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); - for ( let i = 0; i < data.length; i ++ ) { + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if ( data.length > 80 * dim ) { - view[ i ] = data.charCodeAt( i ); + minX = maxX = data[ 0 ]; + minY = maxY = data[ 1 ]; - } + for ( let i = dim; i < outerLen; i += dim ) { - if ( responseType === 'blob' ) { + x = data[ i ]; + y = data[ i + 1 ]; + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; - response = new Blob( [ view.buffer ], { type: mimeType } ); + } - } else { + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max( maxX - minX, maxY - minY ); + invSize = invSize !== 0 ? 1 / invSize : 0; - response = view.buffer; + } - } + earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); - break; + return triangles; - case 'document': + } - const parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); +}; - break; +// create a circular doubly linked list from polygon points in the specified winding order +function linkedList( data, start, end, dim, clockwise ) { - case 'json': + let i, last; - response = JSON.parse( data ); + if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { - break; + for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - default: // 'text' or other + } else { - response = data; + for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); - break; + } - } + if ( last && equals( last, last.next ) ) { - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + removeNode( last ); + last = last.next; - if ( onLoad ) onLoad( response ); + } - scope.manager.itemEnd( url ); + return last; - }, 0 ); +} - } catch ( error ) { +// eliminate colinear or duplicate points +function filterPoints( start, end ) { - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + if ( ! start ) return start; + if ( ! end ) end = start; - if ( onError ) onError( error ); + let p = start, + again; + do { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + again = false; - }, 0 ); + if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { - } + removeNode( p ); + p = end = p.prev; + if ( p === p.next ) break; + again = true; } else { - // Initialise array for duplicate requests - - loading[ url ] = []; + p = p.next; - loading[ url ].push( { + } - onLoad: onLoad, - onProgress: onProgress, - onError: onError + } while ( again || p !== end ); - } ); + return end; - request = new XMLHttpRequest(); +} - request.open( 'GET', url, true ); +// main ear slicing loop which triangulates a polygon (given as a linked list) +function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { - request.addEventListener( 'load', function ( event ) { + if ( ! ear ) return; - const response = this.response; + // interlink polygon nodes in z-order + if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); - const callbacks = loading[ url ]; + let stop = ear, + prev, next; - delete loading[ url ]; + // iterate through ears, slicing them one by one + while ( ear.prev !== ear.next ) { - if ( this.status === 200 || this.status === 0 ) { + prev = ear.prev; + next = ear.next; - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. + if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); + // cut off the triangle + triangles.push( prev.i / dim ); + triangles.push( ear.i / dim ); + triangles.push( next.i / dim ); - // Add to cache only on HTTP success, so that we do not cache - // error response bodies as proper responses to requests. - Cache.add( url, response ); + removeNode( ear ); - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; - const callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); + continue; - } + } - scope.manager.itemEnd( url ); + ear = next; - } else { + // if we looped through the whole remaining polygon and can't find any more ears + if ( ear === stop ) { - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + // try filtering points and slicing again + if ( ! pass ) { - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); - } + // if this didn't work, try curing all small self-intersections locally - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } else if ( pass === 1 ) { - } + ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); + earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - }, false ); + // as a last resort, try splitting the remaining polygon into two - request.addEventListener( 'progress', function ( event ) { + } else if ( pass === 2 ) { - const callbacks = loading[ url ]; + splitEarcut( ear, triangles, dim, minX, minY, invSize ); - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + } - const callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); + break; - } + } - }, false ); + } - request.addEventListener( 'error', function ( event ) { +} - const callbacks = loading[ url ]; +// check whether a polygon node forms a valid ear with adjacent nodes +function isEar( ear ) { - delete loading[ url ]; + const a = ear.prev, + b = ear, + c = ear.next; - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + // now make sure we don't have other points inside the potential ear + let p = ear.next.next; - } + while ( p !== ear.prev ) { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.next; - }, false ); + } - request.addEventListener( 'abort', function ( event ) { + return true; - const callbacks = loading[ url ]; +} - delete loading[ url ]; +function isEarHashed( ear, minX, minY, invSize ) { - for ( let i = 0, il = callbacks.length; i < il; i ++ ) { + const a = ear.prev, + b = ear, + c = ear.next; - const callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear - } + // triangle bbox; min & max are calculated like this for speed + const minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), + minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), + maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), + maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + // z-order range for the current triangle bbox; + const minZ = zOrder( minTX, minTY, minX, minY, invSize ), + maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); - }, false ); + let p = ear.prevZ, + n = ear.nextZ; - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + // look for points inside the triangle in both directions + while ( p && p.z >= minZ && n && n.z <= maxZ ) { - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); + if ( p !== ear.prev && p !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - for ( const header in this.requestHeader ) { + if ( n !== ear.prev && n !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; - request.setRequestHeader( header, this.requestHeader[ header ] ); + } - } + // look for remaining points in decreasing z-order + while ( p && p.z >= minZ ) { - request.send( null ); + if ( p !== ear.prev && p !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; - } + } - scope.manager.itemStart( url ); + // look for remaining points in increasing z-order + while ( n && n.z <= maxZ ) { - return request; + if ( n !== ear.prev && n !== ear.next && + pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && + area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; } - setResponseType( value ) { + return true; - this.responseType = value; - return this; +} - } +// go through all polygon nodes and cure small local self-intersections +function cureLocalIntersections( start, triangles, dim ) { - setMimeType( value ) { + let p = start; + do { - this.mimeType = value; - return this; + const a = p.prev, + b = p.next.next; - } + if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { -} + triangles.push( a.i / dim ); + triangles.push( p.i / dim ); + triangles.push( b.i / dim ); -class ImageLoader extends Loader { + // remove two nodes involved + removeNode( p ); + removeNode( p.next ); - constructor( manager ) { + p = start = b; - super( manager ); + } - } + p = p.next; - load( url, onLoad, onProgress, onError ) { + } while ( p !== start ); - if ( this.path !== undefined ) url = this.path + url; + return filterPoints( p ); - url = this.manager.resolveURL( url ); +} - const scope = this; +// try splitting polygon into two and triangulate them independently +function splitEarcut( start, triangles, dim, minX, minY, invSize ) { - const cached = Cache.get( url ); + // look for a valid diagonal that divides the polygon into two + let a = start; + do { - if ( cached !== undefined ) { + let b = a.next.next; + while ( b !== a.prev ) { - scope.manager.itemStart( url ); + if ( a.i !== b.i && isValidDiagonal( a, b ) ) { - setTimeout( function () { + // split the polygon in two by the diagonal + let c = splitPolygon( a, b ); - if ( onLoad ) onLoad( cached ); + // filter colinear points around the cuts + a = filterPoints( a, a.next ); + c = filterPoints( c, c.next ); - scope.manager.itemEnd( url ); + // run earcut on each half + earcutLinked( a, triangles, dim, minX, minY, invSize ); + earcutLinked( c, triangles, dim, minX, minY, invSize ); + return; - }, 0 ); + } - return cached; + b = b.next; } - const image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + a = a.next; - function onImageLoad() { + } while ( a !== start ); - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); +} - Cache.add( url, this ); +// link every hole into the outer loop, producing a single-ring polygon without holes +function eliminateHoles( data, holeIndices, outerNode, dim ) { - if ( onLoad ) onLoad( this ); + const queue = []; + let i, len, start, end, list; - scope.manager.itemEnd( url ); + for ( i = 0, len = holeIndices.length; i < len; i ++ ) { - } + start = holeIndices[ i ] * dim; + end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; + list = linkedList( data, start, end, dim, false ); + if ( list === list.next ) list.steiner = true; + queue.push( getLeftmost( list ) ); - function onImageError( event ) { + } - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + queue.sort( compareX ); - if ( onError ) onError( event ); + // process holes from left to right + for ( i = 0; i < queue.length; i ++ ) { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + eliminateHole( queue[ i ], outerNode ); + outerNode = filterPoints( outerNode, outerNode.next ); - } + } - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); - - if ( url.substr( 0, 5 ) !== 'data:' ) { + return outerNode; - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; +} - } +function compareX( a, b ) { - scope.manager.itemStart( url ); + return a.x - b.x; - image.src = url; +} - return image; +// find a bridge between vertices that connects hole with an outer ring and link it +function eliminateHole( hole, outerNode ) { - } + outerNode = findHoleBridge( hole, outerNode ); + if ( outerNode ) { -} + const b = splitPolygon( outerNode, hole ); -class CubeTextureLoader extends Loader { + // filter collinear points around the cuts + filterPoints( outerNode, outerNode.next ); + filterPoints( b, b.next ); - constructor( manager ) { + } - super( manager ); +} - } +// David Eberly's algorithm for finding a bridge between hole and outer polygon +function findHoleBridge( hole, outerNode ) { - load( urls, onLoad, onProgress, onError ) { + let p = outerNode; + const hx = hole.x; + const hy = hole.y; + let qx = - Infinity, m; - const texture = new CubeTexture(); + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + do { - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { - let loaded = 0; + const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); + if ( x <= hx && x > qx ) { - function loadTexture( i ) { + qx = x; + if ( x === hx ) { - loader.load( urls[ i ], function ( image ) { + if ( hy === p.y ) return p; + if ( hy === p.next.y ) return p.next; - texture.images[ i ] = image; + } - loaded ++; + m = p.x < p.next.x ? p : p.next; - if ( loaded === 6 ) { + } - texture.needsUpdate = true; + } - if ( onLoad ) onLoad( texture ); + p = p.next; - } + } while ( p !== outerNode ); - }, undefined, onError ); + if ( ! m ) return null; - } + if ( hx === qx ) return m; // hole touches outer segment; pick leftmost endpoint - for ( let i = 0; i < urls.length; ++ i ) { + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point - loadTexture( i ); + const stop = m, + mx = m.x, + my = m.y; + let tanMin = Infinity, tan; - } + p = m; - return texture; + do { - } + if ( hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { -} + tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential -class TextureLoader extends Loader { + if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { - constructor( manager ) { + m = p; + tanMin = tan; - super( manager ); + } - } + } - load( url, onLoad, onProgress, onError ) { + p = p.next; - const texture = new Texture(); + } while ( p !== stop ); - const loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + return m; - loader.load( url, function ( image ) { +} - texture.image = image; +// whether sector in vertex m contains sector in vertex p in the same coordinates +function sectorContainsSector( m, p ) { - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - const isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; + return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; +} - if ( onLoad !== undefined ) { +// interlink polygon nodes in z-order +function indexCurve( start, minX, minY, invSize ) { - onLoad( texture ); + let p = start; + do { - } + if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; - }, onProgress, onError ); + } while ( p !== start ); - return texture; + p.prevZ.nextZ = null; + p.prevZ = null; - } + sortLinked( p ); } -/** - * Extensible curve object. - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +function sortLinked( list ) { -class Curve { + let i, p, q, e, tail, numMerges, pSize, qSize, + inSize = 1; - constructor() { + do { - this.type = 'Curve'; + p = list; + list = null; + tail = null; + numMerges = 0; - this.arcLengthDivisions = 200; + while ( p ) { - } + numMerges ++; + q = p; + pSize = 0; + for ( i = 0; i < inSize; i ++ ) { - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] + pSize ++; + q = q.nextZ; + if ( ! q ) break; - getPoint( /* t, optionalTarget */ ) { + } - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; + qSize = inSize; - } + while ( pSize > 0 || ( qSize > 0 && q ) ) { - // Get point at relative position in curve according to arc length - // - u [0 .. 1] + if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { - getPointAt( u, optionalTarget ) { + e = p; + p = p.nextZ; + pSize --; - const t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); + } else { - } + e = q; + q = q.nextZ; + qSize --; - // Get sequence of points using getPoint( t ) + } - getPoints( divisions = 5 ) { + if ( tail ) tail.nextZ = e; + else list = e; - const points = []; + e.prevZ = tail; + tail = e; - for ( let d = 0; d <= divisions; d ++ ) { + } - points.push( this.getPoint( d / divisions ) ); + p = q; } - return points; + tail.nextZ = null; + inSize *= 2; - } + } while ( numMerges > 1 ); - // Get sequence of points using getPointAt( u ) + return list; - getSpacedPoints( divisions = 5 ) { +} - const points = []; +// z-order of a point given coords and inverse of the longer side of data bbox +function zOrder( x, y, minX, minY, invSize ) { - for ( let d = 0; d <= divisions; d ++ ) { + // coords are transformed into non-negative 15-bit integer range + x = 32767 * ( x - minX ) * invSize; + y = 32767 * ( y - minY ) * invSize; - points.push( this.getPointAt( d / divisions ) ); + x = ( x | ( x << 8 ) ) & 0x00FF00FF; + x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; + x = ( x | ( x << 2 ) ) & 0x33333333; + x = ( x | ( x << 1 ) ) & 0x55555555; - } + y = ( y | ( y << 8 ) ) & 0x00FF00FF; + y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; + y = ( y | ( y << 2 ) ) & 0x33333333; + y = ( y | ( y << 1 ) ) & 0x55555555; - return points; + return x | ( y << 1 ); - } +} - // Get total curve arc length +// find the leftmost node of a polygon ring +function getLeftmost( start ) { - getLength() { + let p = start, + leftmost = start; + do { - const lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; + p = p.next; - } + } while ( p !== start ); - // Get list of cumulative segment lengths + return leftmost; - getLengths( divisions = this.arcLengthDivisions ) { +} - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { +// check if a point lies within a convex triangle +function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { - return this.cacheArcLengths; + return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && + ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && + ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; - } +} - this.needsUpdate = false; +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +function isValidDiagonal( a, b ) { - const cache = []; - let current, last = this.getPoint( 0 ); - let sum = 0; + return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // doesn't intersect other edges + ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible + ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors + equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case - cache.push( 0 ); +} - for ( let p = 1; p <= divisions; p ++ ) { +// signed area of a triangle +function area( p, q, r ) { - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); - } +} - this.cacheArcLengths = cache; +// check if two points are equal +function equals( p1, p2 ) { - return cache; // { sums: cache, sum: sum }; Sum is in the last element. + return p1.x === p2.x && p1.y === p2.y; - } +} - updateArcLengths() { +// check if two segments intersect +function intersects( p1, q1, p2, q2 ) { - this.needsUpdate = true; - this.getLengths(); + const o1 = sign( area( p1, q1, p2 ) ); + const o2 = sign( area( p1, q1, q2 ) ); + const o3 = sign( area( p2, q2, p1 ) ); + const o4 = sign( area( p2, q2, q1 ) ); - } + if ( o1 !== o2 && o3 !== o4 ) return true; // general case - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 - getUtoTmapping( u, distance ) { + return false; - const arcLengths = this.getLengths(); +} - let i = 0; - const il = arcLengths.length; +// for collinear points p, q, r, check if point q lies on segment pr +function onSegment( p, q, r ) { - let targetArcLength; // The targeted u distance value to get + return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); - if ( distance ) { +} - targetArcLength = distance; +function sign( num ) { - } else { + return num > 0 ? 1 : num < 0 ? - 1 : 0; - targetArcLength = u * arcLengths[ il - 1 ]; +} - } +// check if a polygon diagonal intersects any polygon segments +function intersectsPolygon( a, b ) { - // binary search for the index with largest value smaller than target u distance + let p = a; + do { - let low = 0, high = il - 1, comparison; + if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects( p, p.next, a, b ) ) return true; + p = p.next; - while ( low <= high ) { + } while ( p !== a ); - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + return false; - comparison = arcLengths[ i ] - targetArcLength; +} - if ( comparison < 0 ) { +// check if a polygon diagonal is locally inside the polygon +function locallyInside( a, b ) { - low = i + 1; + return area( a.prev, a, a.next ) < 0 ? + area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : + area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; - } else if ( comparison > 0 ) { +} - high = i - 1; +// check if the middle point of a polygon diagonal is inside the polygon +function middleInside( a, b ) { - } else { + let p = a, + inside = false; + const px = ( a.x + b.x ) / 2, + py = ( a.y + b.y ) / 2; + do { - high = i; - break; + if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && + ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) + inside = ! inside; + p = p.next; - // DONE + } while ( p !== a ); - } + return inside; - } +} - i = high; +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; +// if one belongs to the outer ring and another to a hole, it merges it into a single ring +function splitPolygon( a, b ) { - if ( arcLengths[ i ] === targetArcLength ) { + const a2 = new Node( a.i, a.x, a.y ), + b2 = new Node( b.i, b.x, b.y ), + an = a.next, + bp = b.prev; - return i / ( il - 1 ); + a.next = b; + b.prev = a; - } + a2.next = an; + an.prev = a2; - // we could get finer grain at lengths, or use simple interpolation between two points + b2.next = a2; + a2.prev = b2; - const lengthBefore = arcLengths[ i ]; - const lengthAfter = arcLengths[ i + 1 ]; + bp.next = b2; + b2.prev = bp; - const segmentLength = lengthAfter - lengthBefore; + return b2; - // determine where we are between the 'before' and 'after' points +} - const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; +// create a node and optionally link it with previous one (in a circular doubly linked list) +function insertNode( i, x, y, last ) { - // add that fractional amount to t + const p = new Node( i, x, y ); - const t = ( i + segmentFraction ) / ( il - 1 ); + if ( ! last ) { - return t; + p.prev = p; + p.next = p; - } + } else { - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; - getTangent( t, optionalTarget ) { + } - const delta = 0.0001; - let t1 = t - delta; - let t2 = t + delta; + return p; - // Capping in case of danger +} - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; +function removeNode( p ) { - const pt1 = this.getPoint( t1 ); - const pt2 = this.getPoint( t2 ); + p.next.prev = p.prev; + p.prev.next = p.next; - const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); + if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; + if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; - tangent.copy( pt2 ).sub( pt1 ).normalize(); +} - return tangent; +function Node( i, x, y ) { - } + // vertex index in coordinates array + this.i = i; - getTangentAt( u, optionalTarget ) { + // vertex coordinates + this.x = x; + this.y = y; - const t = this.getUtoTmapping( u ); - return this.getTangent( t, optionalTarget ); + // previous and next vertex nodes in a polygon ring + this.prev = null; + this.next = null; - } + // z-order curve value + this.z = null; - computeFrenetFrames( segments, closed ) { + // previous and next nodes in z-order + this.prevZ = null; + this.nextZ = null; - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf + // indicates whether this is a steiner point + this.steiner = false; - const normal = new Vector3(); +} - const tangents = []; - const normals = []; - const binormals = []; +function signedArea( data, start, end, dim ) { - const vec = new Vector3(); - const mat = new Matrix4(); + let sum = 0; + for ( let i = start, j = end - dim; i < end; i += dim ) { - // compute the tangent vectors for each segment on the curve + sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); + j = i; - for ( let i = 0; i <= segments; i ++ ) { + } - const u = i / segments; + return sum; - tangents[ i ] = this.getTangentAt( u, new Vector3() ); - tangents[ i ].normalize(); +} - } +class ShapeUtils { - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component + // calculate area of the contour polygon - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - let min = Number.MAX_VALUE; - const tx = Math.abs( tangents[ 0 ].x ); - const ty = Math.abs( tangents[ 0 ].y ); - const tz = Math.abs( tangents[ 0 ].z ); + static area( contour ) { - if ( tx <= min ) { + const n = contour.length; + let a = 0.0; - min = tx; - normal.set( 1, 0, 0 ); + for ( let p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } - if ( ty <= min ) { + return a * 0.5; - min = ty; - normal.set( 0, 1, 0 ); - - } + } - if ( tz <= min ) { + static isClockWise( pts ) { - normal.set( 0, 0, 1 ); + return ShapeUtils.area( pts ) < 0; - } + } - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + static triangulateShape( contour, holes ) { - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] + const holeIndices = []; // array of hole indices + const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + removeDupEndPts( contour ); + addContour( vertices, contour ); - // compute the slowly-varying normal and binormal vectors for each segment on the curve + // - for ( let i = 1; i <= segments; i ++ ) { + let holeIndex = contour.length; - normals[ i ] = normals[ i - 1 ].clone(); + holes.forEach( removeDupEndPts ); - binormals[ i ] = binormals[ i - 1 ].clone(); + for ( let i = 0; i < holes.length; i ++ ) { - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + holeIndices.push( holeIndex ); + holeIndex += holes[ i ].length; + addContour( vertices, holes[ i ] ); - if ( vec.length() > Number.EPSILON ) { + } - vec.normalize(); + // - const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + const triangles = Earcut.triangulate( vertices, holeIndices ); - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + // - } + for ( let i = 0; i < triangles.length; i += 3 ) { - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + faces.push( triangles.slice( i, i + 3 ) ); } - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - - if ( closed === true ) { - - let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; + return faces; - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { + } - theta = - theta; +} - } +function removeDupEndPts( points ) { - for ( let i = 1; i <= segments; i ++ ) { + const l = points.length; - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - } + points.pop(); - } + } - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; +} - } +function addContour( vertices, contour ) { - clone() { + for ( let i = 0; i < contour.length; i ++ ) { - return new this.constructor().copy( this ); + vertices.push( contour[ i ].x ); + vertices.push( contour[ i ].y ); } - copy( source ) { +} - this.arcLengthDivisions = source.arcLengthDivisions; +/** + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * depth: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline (including bevelOffset) is bevel + * bevelOffset: , // how far from shape outline does bevel start + * bevelSegments: , // number of bevel layers + * + * extrudePath: // curve to extrude shape along + * + * UVGenerator: // object that provides UV generator functions + * + * } + */ - return this; +class ExtrudeGeometry extends BufferGeometry { - } + constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { - toJSON() { + super(); - const data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } + this.type = 'ExtrudeGeometry'; + + this.parameters = { + shapes: shapes, + options: options }; - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - return data; + const scope = this; - } + const verticesArray = []; + const uvArray = []; - fromJSON( json ) { + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - this.arcLengthDivisions = json.arcLengthDivisions; + const shape = shapes[ i ]; + addShape( shape ); - return this; + } - } + // build geometry -} + this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); -class EllipseCurve extends Curve { + this.computeVertexNormals(); - constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { + // functions - super(); + function addShape( shape ) { - this.type = 'EllipseCurve'; + const placeholder = []; - this.aX = aX; - this.aY = aY; + // options - this.xRadius = xRadius; - this.yRadius = yRadius; + const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + const steps = options.steps !== undefined ? options.steps : 1; + let depth = options.depth !== undefined ? options.depth : 1; - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; + let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; + let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; + let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; + let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; + let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - this.aClockwise = aClockwise; + const extrudePath = options.extrudePath; - this.aRotation = aRotation; + const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; - } + // deprecated options - getPoint( t, optionalTarget ) { + if ( options.amount !== undefined ) { - const point = optionalTarget || new Vector2(); + console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); + depth = options.amount; - const twoPi = Math.PI * 2; - let deltaAngle = this.aEndAngle - this.aStartAngle; - const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + } - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + // - if ( deltaAngle < Number.EPSILON ) { + let extrudePts, extrudeByPath = false; + let splineTube, binormal, normal, position2; - if ( samePoints ) { + if ( extrudePath ) { - deltaAngle = 0; + extrudePts = extrudePath.getSpacedPoints( steps ); - } else { + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - deltaAngle = twoPi; + // SETUP TNB variables - } + // TODO1 - have a .isClosed in spline? - } + splineTube = extrudePath.computeFrenetFrames( steps, false ); - if ( this.aClockwise === true && ! samePoints ) { + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - if ( deltaAngle === twoPi ) { + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); - deltaAngle = - twoPi; + } - } else { + // Safeguards if bevels are not enabled - deltaAngle = deltaAngle - twoPi; + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + bevelOffset = 0; } - } + // Variables initialization - const angle = this.aStartAngle + t * deltaAngle; - let x = this.aX + this.xRadius * Math.cos( angle ); - let y = this.aY + this.yRadius * Math.sin( angle ); + const shapePoints = shape.extractPoints( curveSegments ); - if ( this.aRotation !== 0 ) { + let vertices = shapePoints.shape; + const holes = shapePoints.holes; - const cos = Math.cos( this.aRotation ); - const sin = Math.sin( this.aRotation ); + const reverse = ! ShapeUtils.isClockWise( vertices ); - const tx = x - this.aX; - const ty = y - this.aY; + if ( reverse ) { - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; + vertices = vertices.reverse(); - } + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - return point.set( x, y ); + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - } + const ahole = holes[ h ]; - copy( source ) { + if ( ShapeUtils.isClockWise( ahole ) ) { - super.copy( source ); + holes[ h ] = ahole.reverse(); - this.aX = source.aX; - this.aY = source.aY; + } - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; + } - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; + } - this.aClockwise = source.aClockwise; - this.aRotation = source.aRotation; + const faces = ShapeUtils.triangulateShape( vertices, holes ); - return this; + /* Vertices */ - } + const contour = vertices; // vertices has all points but contour has only points of circumference - toJSON() { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - const data = super.toJSON(); + const ahole = holes[ h ]; - data.aX = this.aX; - data.aY = this.aY; + vertices = vertices.concat( ahole ); - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; + } - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; - data.aClockwise = this.aClockwise; + function scalePt2( pt, vec, size ) { - data.aRotation = this.aRotation; + if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); - return data; + return vec.clone().multiplyScalar( size ).add( pt ); - } + } - fromJSON( json ) { + const vlen = vertices.length, flen = faces.length; - super.fromJSON( json ); - this.aX = json.aX; - this.aY = json.aY; + // Find directions for point movement - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; + function getBevelVec( inPt, inPrev, inNext ) { - this.aClockwise = json.aClockwise; + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. - this.aRotation = json.aRotation; + let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt - return this; + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html - } + const v_prev_x = inPt.x - inPrev.x, + v_prev_y = inPt.y - inPrev.y; + const v_next_x = inNext.x - inPt.x, + v_next_y = inNext.y - inPt.y; -} + const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); -EllipseCurve.prototype.isEllipseCurve = true; + // check for collinear edges + const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); -class ArcCurve extends EllipseCurve { + if ( Math.abs( collinear0 ) > Number.EPSILON ) { - constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + // not collinear - super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + // length of vectors for normalizing - this.type = 'ArcCurve'; + const v_prev_len = Math.sqrt( v_prev_lensq ); + const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - } + // shift adjacent points by unit vectors to the left -} + const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); -ArcCurve.prototype.isArcCurve = true; + const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); -/** - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ + // scaling factor for v_prev to intersection point + const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); -/* -Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM + // vector from inPt to intersection point -This CubicPoly class could be used for reusing some variables and calculations, -but for three.js curve use, it could be possible inlined and flatten into a single function call -which can be placed in CurveUtils. -*/ + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); -function CubicPoly() { + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { - let c0 = 0, c1 = 0, c2 = 0, c3 = 0; + return new Vector2( v_trans_x, v_trans_y ); - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { + } else { - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; + shrink_by = Math.sqrt( v_trans_lensq / 2 ); - } + } - return { + } else { - initCatmullRom: function ( x0, x1, x2, x3, tension ) { + // handle special case of collinear edges - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + let direction_eq = false; // assumes: opposite - }, + if ( v_prev_x > Number.EPSILON ) { - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { + if ( v_next_x > Number.EPSILON ) { - // compute tangents when parameterized in [t1,t2] - let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + direction_eq = true; - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + } - init( x1, x2, t1, t2 ); + } else { - }, + if ( v_prev_x < - Number.EPSILON ) { - calc: function ( t ) { + if ( v_next_x < - Number.EPSILON ) { - const t2 = t * t; - const t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; + direction_eq = true; - } + } - }; + } else { -} + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { -// + direction_eq = true; -const tmp = new Vector3(); -const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); + } -class CatmullRomCurve3 extends Curve { + } - constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { + } - super(); + if ( direction_eq ) { - this.type = 'CatmullRomCurve3'; + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); - this.points = points; - this.closed = closed; - this.curveType = curveType; - this.tension = tension; + } else { - } + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); - getPoint( t, optionalTarget = new Vector3() ) { + } - const point = optionalTarget; + } - const points = this.points; - const l = points.length; + return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - const p = ( l - ( this.closed ? 0 : 1 ) ) * t; - let intPoint = Math.floor( p ); - let weight = p - intPoint; + } - if ( this.closed ) { - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; + const contourMovements = []; - } else if ( weight === 0 && intPoint === l - 1 ) { + for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - intPoint = l - 2; - weight = 1; + if ( j === il ) j = 0; + if ( k === il ) k = 0; - } + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) - let p0, p3; // 4 points (p1 & p2 defined below) + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - if ( this.closed || intPoint > 0 ) { + } - p0 = points[ ( intPoint - 1 ) % l ]; + const holesMovements = []; + let oneHoleMovements, verticesMovements = contourMovements.concat(); - } else { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + const ahole = holes[ h ]; - } + oneHoleMovements = []; - const p1 = points[ intPoint % l ]; - const p2 = points[ ( intPoint + 1 ) % l ]; + for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - if ( this.closed || intPoint + 2 < l ) { + if ( j === il ) j = 0; + if ( k === il ) k = 0; - p3 = points[ ( intPoint + 2 ) % l ]; + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - } else { + } - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); - } + } - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - // init Centripetal / Chordal Catmull-Rom - const pow = this.curveType === 'chordal' ? 0.5 : 0.25; - let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + // Loop bevelSegments, 1 for the front, 1 for the back - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + for ( let b = 0; b < bevelSegments; b ++ ) { - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + //for ( b = bevelSegments; b > 0; b -- ) { - } else if ( this.curveType === 'catmullrom' ) { + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - - } + // contract shape - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + for ( let i = 0, il = contour.length; i < il; i ++ ) { - return point; + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - } + v( vert.x, vert.y, - z ); - copy( source ) { + } - super.copy( source ); + // expand holes - this.points = []; + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - const point = source.points[ i ]; + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - this.points.push( point.clone() ); + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } + v( vert.x, vert.y, - z ); - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; + } - return this; + } - } + } - toJSON() { + const bs = bevelSize + bevelOffset; - const data = super.toJSON(); + // Back facing vertices - data.points = []; + for ( let i = 0; i < vlen; i ++ ) { - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - const point = this.points[ i ]; - data.points.push( point.toArray() ); + if ( ! extrudeByPath ) { - } + v( vert.x, vert.y, 0 ); - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; + } else { - return data; + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - } + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - fromJSON( json ) { + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - super.fromJSON( json ); + v( position2.x, position2.y, position2.z ); - this.points = []; + } - for ( let i = 0, l = json.points.length; i < l; i ++ ) { + } - const point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); + // Add stepped vertices... + // Including front facing vertices - } + for ( let s = 1; s <= steps; s ++ ) { - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; + for ( let i = 0; i < vlen; i ++ ) { - return this; + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - } + if ( ! extrudeByPath ) { -} + v( vert.x, vert.y, depth / steps * s ); -CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + } else { -/** - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); -function CatmullRom( t, p0, p1, p2, p3 ) { + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - const v0 = ( p2 - p0 ) * 0.5; - const v1 = ( p3 - p1 ) * 0.5; - const t2 = t * t; - const t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); -} + v( position2.x, position2.y, position2.z ); -// + } -function QuadraticBezierP0( t, p ) { + } - const k = 1 - t; - return k * k * p; + } -} -function QuadraticBezierP1( t, p ) { + // Add bevel segments planes - return 2 * ( 1 - t ) * t * p; + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( let b = bevelSegments - 1; b >= 0; b -- ) { -} + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; -function QuadraticBezierP2( t, p ) { + // contract shape - return t * t * p; + for ( let i = 0, il = contour.length; i < il; i ++ ) { -} + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, depth + z ); -function QuadraticBezier( t, p0, p1, p2 ) { + } - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); + // expand holes -} + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { -// + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; -function CubicBezierP0( t, p ) { + for ( let i = 0, il = ahole.length; i < il; i ++ ) { - const k = 1 - t; - return k * k * k * p; + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); -} + if ( ! extrudeByPath ) { -function CubicBezierP1( t, p ) { + v( vert.x, vert.y, depth + z ); - const k = 1 - t; - return 3 * k * k * t * p; + } else { -} + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); -function CubicBezierP2( t, p ) { + } - return 3 * ( 1 - t ) * t * t * p; + } -} + } -function CubicBezierP3( t, p ) { + } - return t * t * t * p; + /* Faces */ -} + // Top and bottom faces -function CubicBezier( t, p0, p1, p2, p3 ) { + buildLidFaces(); - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); + // Sides faces -} + buildSideFaces(); -class CubicBezierCurve extends Curve { - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { + ///// Internal functions - super(); + function buildLidFaces() { - this.type = 'CubicBezierCurve'; + const start = verticesArray.length / 3; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + if ( bevelEnabled ) { - } + let layer = 0; // steps + 1 + let offset = vlen * layer; - getPoint( t, optionalTarget = new Vector2() ) { + // Bottom faces - const point = optionalTarget; + for ( let i = 0; i < flen; i ++ ) { - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + const face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); + } - return point; + layer = steps + bevelSegments * 2; + offset = vlen * layer; - } + // Top faces - copy( source ) { + for ( let i = 0; i < flen; i ++ ) { - super.copy( source ); + const face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + } - return this; + } else { - } + // Bottom faces - toJSON() { + for ( let i = 0; i < flen; i ++ ) { - const data = super.toJSON(); + const face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + } - return data; + // Top faces - } + for ( let i = 0; i < flen; i ++ ) { - fromJSON( json ) { + const face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - super.fromJSON( json ); + } - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + } - return this; + scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - } + } -} + // Create faces for the z-sides of the shape -CubicBezierCurve.prototype.isCubicBezierCurve = true; + function buildSideFaces() { -class CubicBezierCurve3 extends Curve { + const start = verticesArray.length / 3; + let layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { - super(); + const ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); - this.type = 'CubicBezierCurve3'; + //, true + layeroffset += ahole.length; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + } - } - getPoint( t, optionalTarget = new Vector3() ) { + scope.addGroup( start, verticesArray.length / 3 - start, 1 ); - const point = optionalTarget; - const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + } - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); + function sidewalls( contour, layeroffset ) { - return point; + let i = contour.length; - } + while ( -- i >= 0 ) { - copy( source ) { + const j = i; + let k = i - 1; + if ( k < 0 ) k = contour.length - 1; - super.copy( source ); + //console.log('b', i,j, i-1, k,vertices.length); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { - return this; + const slen1 = vlen * s; + const slen2 = vlen * ( s + 1 ); - } + const a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; - toJSON() { + f4( a, b, c, d ); - const data = super.toJSON(); + } - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + } - return data; + } - } + function v( x, y, z ) { - fromJSON( json ) { + placeholder.push( x ); + placeholder.push( y ); + placeholder.push( z ); - super.fromJSON( json ); + } - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); - return this; + function f3( a, b, c ) { - } + addVertex( a ); + addVertex( b ); + addVertex( c ); -} + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); -CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); -class LineCurve extends Curve { + } - constructor( v1 = new Vector2(), v2 = new Vector2() ) { + function f4( a, b, c, d ) { - super(); + addVertex( a ); + addVertex( b ); + addVertex( d ); - this.type = 'LineCurve'; + addVertex( b ); + addVertex( c ); + addVertex( d ); - this.v1 = v1; - this.v2 = v2; - } + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); - getPoint( t, optionalTarget = new Vector2() ) { + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 3 ] ); - const point = optionalTarget; + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); + addUV( uvs[ 3 ] ); - if ( t === 1 ) { + } - point.copy( this.v2 ); + function addVertex( index ) { - } else { + verticesArray.push( placeholder[ index * 3 + 0 ] ); + verticesArray.push( placeholder[ index * 3 + 1 ] ); + verticesArray.push( placeholder[ index * 3 + 2 ] ); - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + } - } - return point; + function addUV( vector2 ) { - } + uvArray.push( vector2.x ); + uvArray.push( vector2.y ); - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { + } - return this.getPoint( u, optionalTarget ); + } } - getTangent( t, optionalTarget ) { + toJSON() { - const tangent = optionalTarget || new Vector2(); + const data = super.toJSON(); - tangent.copy( this.v2 ).sub( this.v1 ).normalize(); + const shapes = this.parameters.shapes; + const options = this.parameters.options; - return tangent; + return toJSON$1( shapes, options, data ); } - copy( source ) { - - super.copy( source ); - - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + static fromJSON( data, shapes ) { - return this; - - } + const geometryShapes = []; - toJSON() { + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - const data = super.toJSON(); + const shape = shapes[ data.shapes[ j ] ]; - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + geometryShapes.push( shape ); - return data; + } - } + const extrudePath = data.options.extrudePath; - fromJSON( json ) { + if ( extrudePath !== undefined ) { - super.fromJSON( json ); + data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } - return this; + return new ExtrudeGeometry( geometryShapes, data.options ); } } -LineCurve.prototype.isLineCurve = true; - -class LineCurve3 extends Curve { +const WorldUVGenerator = { - constructor( v1 = new Vector3(), v2 = new Vector3() ) { + generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { - super(); + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; - this.type = 'LineCurve3'; - this.isLineCurve3 = true; + return [ + new Vector2( a_x, a_y ), + new Vector2( b_x, b_y ), + new Vector2( c_x, c_y ) + ]; - this.v1 = v1; - this.v2 = v2; + }, - } - getPoint( t, optionalTarget = new Vector3() ) { + generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - const point = optionalTarget; + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const a_z = vertices[ indexA * 3 + 2 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const b_z = vertices[ indexB * 3 + 2 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; + const c_z = vertices[ indexC * 3 + 2 ]; + const d_x = vertices[ indexD * 3 ]; + const d_y = vertices[ indexD * 3 + 1 ]; + const d_z = vertices[ indexD * 3 + 2 ]; - if ( t === 1 ) { + if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { - point.copy( this.v2 ); + return [ + new Vector2( a_x, 1 - a_z ), + new Vector2( b_x, 1 - b_z ), + new Vector2( c_x, 1 - c_z ), + new Vector2( d_x, 1 - d_z ) + ]; } else { - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + return [ + new Vector2( a_y, 1 - a_z ), + new Vector2( b_y, 1 - b_z ), + new Vector2( c_y, 1 - c_z ), + new Vector2( d_y, 1 - d_z ) + ]; } - return point; - } - // Line curve is linear, so we can overwrite default getPointAt - getPointAt( u, optionalTarget ) { - - return this.getPoint( u, optionalTarget ); - } - copy( source ) { +}; - super.copy( source ); +function toJSON$1( shapes, options, data ) { - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + data.shapes = []; - return this; + if ( Array.isArray( shapes ) ) { - } - toJSON() { + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - const data = super.toJSON(); + const shape = shapes[ i ]; - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + data.shapes.push( shape.uuid ); - return data; + } - } - fromJSON( json ) { + } else { - super.fromJSON( json ); + data.shapes.push( shapes.uuid ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } - return this; + if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); - } + return data; } -class QuadraticBezierCurve extends Curve { +class ShapeGeometry extends BufferGeometry { - constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { + constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { super(); + this.type = 'ShapeGeometry'; - this.type = 'QuadraticBezierCurve'; - - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + this.parameters = { + shapes: shapes, + curveSegments: curveSegments + }; - } + // buffers - getPoint( t, optionalTarget = new Vector2() ) { + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - const point = optionalTarget; + // helper variables - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + let groupStart = 0; + let groupCount = 0; - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); + // allow single and array values for "shapes" parameter - return point; + if ( Array.isArray( shapes ) === false ) { - } + addShape( shapes ); - copy( source ) { + } else { - super.copy( source ); + for ( let i = 0; i < shapes.length; i ++ ) { - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + addShape( shapes[ i ] ); - return this; + this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support - } + groupStart += groupCount; + groupCount = 0; - toJSON() { + } - const data = super.toJSON(); + } - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + // build geometry - return data; + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - } - fromJSON( json ) { + // helper functions - super.fromJSON( json ); + function addShape( shape ) { - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + const indexOffset = vertices.length / 3; + const points = shape.extractPoints( curveSegments ); - return this; + let shapeVertices = points.shape; + const shapeHoles = points.holes; - } + // check direction of vertices -} + if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { -QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; + shapeVertices = shapeVertices.reverse(); -class QuadraticBezierCurve3 extends Curve { + } - constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - super(); + const shapeHole = shapeHoles[ i ]; - this.type = 'QuadraticBezierCurve3'; + if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + shapeHoles[ i ] = shapeHole.reverse(); - } + } - getPoint( t, optionalTarget = new Vector3() ) { + } - const point = optionalTarget; + const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); - const v0 = this.v0, v1 = this.v1, v2 = this.v2; + // join vertices of inner and outer paths to a single array - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { - return point; + const shapeHole = shapeHoles[ i ]; + shapeVertices = shapeVertices.concat( shapeHole ); - } + } - copy( source ) { + // vertices, normals, uvs - super.copy( source ); + for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + const vertex = shapeVertices[ i ]; - return this; + vertices.push( vertex.x, vertex.y, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( vertex.x, vertex.y ); // world uvs - } + } - toJSON() { + // incides - const data = super.toJSON(); + for ( let i = 0, l = faces.length; i < l; i ++ ) { - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + const face = faces[ i ]; - return data; + const a = face[ 0 ] + indexOffset; + const b = face[ 1 ] + indexOffset; + const c = face[ 2 ] + indexOffset; + + indices.push( a, b, c ); + groupCount += 3; + + } + + } } - fromJSON( json ) { + toJSON() { - super.fromJSON( json ); + const data = super.toJSON(); - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + const shapes = this.parameters.shapes; - return this; + return toJSON( shapes, data ); } -} + static fromJSON( data, shapes ) { -QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + const geometryShapes = []; -class SplineCurve extends Curve { + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { - constructor( points = [] ) { + const shape = shapes[ data.shapes[ j ] ]; - super(); + geometryShapes.push( shape ); - this.type = 'SplineCurve'; + } - this.points = points; + return new ShapeGeometry( geometryShapes, data.curveSegments ); } - getPoint( t, optionalTarget = new Vector2() ) { +} - const point = optionalTarget; +function toJSON( shapes, data ) { - const points = this.points; - const p = ( points.length - 1 ) * t; + data.shapes = []; - const intPoint = Math.floor( p ); - const weight = p - intPoint; + if ( Array.isArray( shapes ) ) { - const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - const p1 = points[ intPoint ]; - const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + for ( let i = 0, l = shapes.length; i < l; i ++ ) { - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); + const shape = shapes[ i ]; - return point; + data.shapes.push( shape.uuid ); - } + } - copy( source ) { + } else { - super.copy( source ); + data.shapes.push( shapes.uuid ); - this.points = []; + } - for ( let i = 0, l = source.points.length; i < l; i ++ ) { + return data; - const point = source.points[ i ]; +} - this.points.push( point.clone() ); +class ShadowMaterial extends Material { - } + constructor( parameters ) { - return this; + super(); - } + this.type = 'ShadowMaterial'; - toJSON() { + this.color = new Color( 0x000000 ); + this.transparent = true; - const data = super.toJSON(); + this.setValues( parameters ); - data.points = []; + } - for ( let i = 0, l = this.points.length; i < l; i ++ ) { + copy( source ) { - const point = this.points[ i ]; - data.points.push( point.toArray() ); + super.copy( source ); - } + this.color.copy( source.color ); - return data; + return this; } - fromJSON( json ) { - - super.fromJSON( json ); +} - this.points = []; +ShadowMaterial.prototype.isShadowMaterial = true; - for ( let i = 0, l = json.points.length; i < l; i ++ ) { +class RawShaderMaterial extends ShaderMaterial { - const point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); + constructor( parameters ) { - } + super( parameters ); - return this; + this.type = 'RawShaderMaterial'; } } -SplineCurve.prototype.isSplineCurve = true; +RawShaderMaterial.prototype.isRawShaderMaterial = true; -var Curves = /*#__PURE__*/Object.freeze({ - __proto__: null, - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve -}); +class MeshStandardMaterial extends Material { -/************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + constructor( parameters ) { -class CurvePath extends Curve { + super(); - constructor() { + this.defines = { 'STANDARD': '' }; - super(); + this.type = 'MeshStandardMaterial'; - this.type = 'CurvePath'; + this.color = new Color( 0xffffff ); // diffuse + this.roughness = 1.0; + this.metalness = 0.0; - this.curves = []; - this.autoClose = false; // Automatically closes the path + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - add( curve ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - this.curves.push( curve ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - } + this.bumpMap = null; + this.bumpScale = 1; - closePath() { + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - // Add a line curve if start and end of lines are not connected - const startPoint = this.curves[ 0 ].getPoint( 0 ); - const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - if ( ! startPoint.equals( endPoint ) ) { + this.roughnessMap = null; - this.curves.push( new LineCurve( endPoint, startPoint ) ); + this.metalnessMap = null; - } + this.alphaMap = null; - } + this.envMap = null; + this.envMapIntensity = 1.0; - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') + this.flatShading = false; - getPoint( t ) { + this.setValues( parameters ); - const d = t * this.getLength(); - const curveLengths = this.getCurveLengths(); - let i = 0; + } - // To think about boundaries points. + copy( source ) { - while ( i < curveLengths.length ) { + super.copy( source ); - if ( curveLengths[ i ] >= d ) { + this.defines = { 'STANDARD': '' }; - const diff = curveLengths[ i ] - d; - const curve = this.curves[ i ]; + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; - const segmentLength = curve.getLength(); - const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + this.map = source.map; - return curve.getPointAt( u ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - } + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - i ++; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - } + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - return null; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - // loop where sum != 0, sum > d , sum+1 0 !== value > 0 ) { - for ( let j = 0; j < pts.length; j ++ ) { + this.version ++; - const point = pts[ j ]; + } - if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates + this._sheen = value; - points.push( point ); - last = point; + } - } + get clearcoat() { - } + return this._clearcoat; - if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { + } - points.push( points[ 0 ] ); + set clearcoat( value ) { + + if ( this._clearcoat > 0 !== value > 0 ) { + + this.version ++; } - return points; + this._clearcoat = value; } - copy( source ) { + get transmission() { - super.copy( source ); + return this._transmission; - this.curves = []; + } - for ( let i = 0, l = source.curves.length; i < l; i ++ ) { + set transmission( value ) { - const curve = source.curves[ i ]; + if ( this._transmission > 0 !== value > 0 ) { - this.curves.push( curve.clone() ); + this.version ++; } - this.autoClose = source.autoClose; - - return this; + this._transmission = value; } - toJSON() { - - const data = super.toJSON(); - - data.autoClose = this.autoClose; - data.curves = []; - - for ( let i = 0, l = this.curves.length; i < l; i ++ ) { + copy( source ) { - const curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); + super.copy( source ); - } + this.defines = { - return data; + 'STANDARD': '', + 'PHYSICAL': '' - } + }; - fromJSON( json ) { + this.clearcoat = source.clearcoat; + this.clearcoatMap = source.clearcoatMap; + this.clearcoatRoughness = source.clearcoatRoughness; + this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; + this.clearcoatNormalMap = source.clearcoatNormalMap; + this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); - super.fromJSON( json ); + this.ior = source.ior; - this.autoClose = json.autoClose; - this.curves = []; + this.sheen = source.sheen; + this.sheenColor.copy( source.sheenColor ); + this.sheenColorMap = source.sheenColorMap; + this.sheenRoughness = source.sheenRoughness; + this.sheenRoughnessMap = source.sheenRoughnessMap; - for ( let i = 0, l = json.curves.length; i < l; i ++ ) { + this.transmission = source.transmission; + this.transmissionMap = source.transmissionMap; - const curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); + this.thickness = source.thickness; + this.thicknessMap = source.thicknessMap; + this.attenuationDistance = source.attenuationDistance; + this.attenuationColor.copy( source.attenuationColor ); - } + this.specularIntensity = source.specularIntensity; + this.specularIntensityMap = source.specularIntensityMap; + this.specularColor.copy( source.specularColor ); + this.specularColorMap = source.specularColorMap; return this; @@ -35727,362 +35658,442 @@ class CurvePath extends Curve { } -class Path extends CurvePath { +MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - constructor( points ) { +class MeshPhongMaterial extends Material { - super(); - this.type = 'Path'; + constructor( parameters ) { - this.currentPoint = new Vector2(); + super(); - if ( points ) { + this.type = 'MeshPhongMaterial'; - this.setFromPoints( points ); + this.color = new Color( 0xffffff ); // diffuse + this.specular = new Color( 0x111111 ); + this.shininess = 30; - } + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - setFromPoints( points ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - this.moveTo( points[ 0 ].x, points[ 0 ].y ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - for ( let i = 1, l = points.length; i < l; i ++ ) { + this.bumpMap = null; + this.bumpScale = 1; - this.lineTo( points[ i ].x, points[ i ].y ); + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - return this; + this.specularMap = null; - } + this.alphaMap = null; - moveTo( x, y ) { + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - return this; + this.flatShading = false; + + this.setValues( parameters ); } - lineTo( x, y ) { + copy( source ) { - const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); + super.copy( source ); - this.currentPoint.set( x, y ); + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - return this; + this.map = source.map; - } + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - quadraticCurveTo( aCPx, aCPy, aX, aY ) { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - const curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - this.curves.push( curve ); + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - this.currentPoint.set( aX, aY ); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - return this; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.specularMap = source.specularMap; - bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + this.alphaMap = source.alphaMap; - const curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - this.curves.push( curve ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - this.currentPoint.set( aX, aY ); + this.flatShading = source.flatShading; return this; } - splineThru( pts /*Array of Vector*/ ) { - - const npts = [ this.currentPoint.clone() ].concat( pts ); +} - const curve = new SplineCurve( npts ); - this.curves.push( curve ); +MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - this.currentPoint.copy( pts[ pts.length - 1 ] ); +class MeshToonMaterial extends Material { - return this; + constructor( parameters ) { - } + super(); - arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.defines = { 'TOON': '' }; - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + this.type = 'MeshToonMaterial'; - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + this.color = new Color( 0xffffff ); - return this; + this.map = null; + this.gradientMap = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - return this; + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - const x0 = this.currentPoint.x; - const y0 = this.currentPoint.y; + this.alphaMap = null; - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - return this; + this.setValues( parameters ); } - absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + copy( source ) { - const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + super.copy( source ); - if ( this.curves.length > 0 ) { + this.color.copy( source.color ); - // if a previous curve is present, attempt to join - const firstPoint = curve.getPoint( 0 ); + this.map = source.map; + this.gradientMap = source.gradientMap; - if ( ! firstPoint.equals( this.currentPoint ) ) { + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - this.lineTo( firstPoint.x, firstPoint.y ); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - } + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - this.curves.push( curve ); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - const lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.alphaMap = source.alphaMap; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; return this; } - copy( source ) { - - super.copy( source ); +} - this.currentPoint.copy( source.currentPoint ); +MeshToonMaterial.prototype.isMeshToonMaterial = true; - return this; +class MeshNormalMaterial extends Material { - } + constructor( parameters ) { - toJSON() { + super(); - const data = super.toJSON(); + this.type = 'MeshNormalMaterial'; - data.currentPoint = this.currentPoint.toArray(); + this.bumpMap = null; + this.bumpScale = 1; - return data; + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - fromJSON( json ) { + this.wireframe = false; + this.wireframeLinewidth = 1; - super.fromJSON( json ); + this.fog = false; - this.currentPoint.fromArray( json.currentPoint ); + this.flatShading = false; - return this; + this.setValues( parameters ); } -} + copy( source ) { -class Shape extends Path { + super.copy( source ); - constructor( points ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - super( points ); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - this.uuid = generateUUID(); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - this.type = 'Shape'; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - this.holes = []; + this.flatShading = source.flatShading; + + return this; } - getPointsHoles( divisions ) { +} - const holesPts = []; +MeshNormalMaterial.prototype.isMeshNormalMaterial = true; - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { +class MeshLambertMaterial extends Material { - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + constructor( parameters ) { - } + super(); - return holesPts; + this.type = 'MeshLambertMaterial'; - } + this.color = new Color( 0xffffff ); // diffuse - // get points of shape and holes (keypoints based on segments parameter) + this.map = null; - extractPoints( divisions ) { + this.lightMap = null; + this.lightMapIntensity = 1.0; - return { + this.aoMap = null; + this.aoMapIntensity = 1.0; - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - }; + this.specularMap = null; - } + this.alphaMap = null; - copy( source ) { + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - super.copy( source ); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - this.holes = []; + this.setValues( parameters ); - for ( let i = 0, l = source.holes.length; i < l; i ++ ) { + } - const hole = source.holes[ i ]; + copy( source ) { - this.holes.push( hole.clone() ); + super.copy( source ); - } + this.color.copy( source.color ); - return this; + this.map = source.map; - } + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - toJSON() { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - const data = super.toJSON(); + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - data.uuid = this.uuid; - data.holes = []; + this.specularMap = source.specularMap; - for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + this.alphaMap = source.alphaMap; - const hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - return data; + return this; } - fromJSON( json ) { - - super.fromJSON( json ); +} - this.uuid = json.uuid; - this.holes = []; +MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - for ( let i = 0, l = json.holes.length; i < l; i ++ ) { +class MeshMatcapMaterial extends Material { - const hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); + constructor( parameters ) { - } + super(); - return this; + this.defines = { 'MATCAP': '' }; - } + this.type = 'MeshMatcapMaterial'; -} + this.color = new Color( 0xffffff ); // diffuse -class Light extends Object3D { + this.matcap = null; - constructor( color, intensity = 1 ) { + this.map = null; - super(); + this.bumpMap = null; + this.bumpScale = 1; - this.type = 'Light'; + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - this.color = new Color( color ); - this.intensity = intensity; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.alphaMap = null; - dispose() { + this.flatShading = false; - // Empty here in base class; some subclasses override. + this.setValues( parameters ); } + copy( source ) { super.copy( source ); - this.color.copy( source.color ); - this.intensity = source.intensity; + this.defines = { 'MATCAP': '' }; - return this; + this.color.copy( source.color ); - } + this.matcap = source.matcap; - toJSON( meta ) { + this.map = source.map; - const data = super.toJSON( meta ); + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; + this.alphaMap = source.alphaMap; - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); + this.flatShading = source.flatShading; - return data; + return this; } } -Light.prototype.isLight = true; +MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; -class HemisphereLight extends Light { +class LineDashedMaterial extends LineBasicMaterial { - constructor( skyColor, groundColor, intensity ) { + constructor( parameters ) { - super( skyColor, intensity ); + super(); - this.type = 'HemisphereLight'; + this.type = 'LineDashedMaterial'; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - this.groundColor = new Color( groundColor ); + this.setValues( parameters ); } copy( source ) { - Light.prototype.copy.call( this, source ); + super.copy( source ); - this.groundColor.copy( source.groundColor ); + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; return this; @@ -36090,4074 +36101,3954 @@ class HemisphereLight extends Light { } -HemisphereLight.prototype.isHemisphereLight = true; +LineDashedMaterial.prototype.isLineDashedMaterial = true; -const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); -const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); -const _lookTarget$1 = /*@__PURE__*/ new Vector3(); +const materialLib = { + ShadowMaterial, + SpriteMaterial, + RawShaderMaterial, + ShaderMaterial, + PointsMaterial, + MeshPhysicalMaterial, + MeshStandardMaterial, + MeshPhongMaterial, + MeshToonMaterial, + MeshNormalMaterial, + MeshLambertMaterial, + MeshDepthMaterial, + MeshDistanceMaterial, + MeshBasicMaterial, + MeshMatcapMaterial, + LineDashedMaterial, + LineBasicMaterial, + Material +}; -class LightShadow { +Material.fromType = function ( type ) { - constructor( camera ) { + return new materialLib[ type ](); - this.camera = camera; +}; - this.bias = 0; - this.normalBias = 0; - this.radius = 1; +const AnimationUtils = { - this.mapSize = new Vector2( 512, 512 ); + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function ( array, from, to ) { - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); + if ( AnimationUtils.isTypedArray( array ) ) { - this.autoUpdate = true; - this.needsUpdate = false; + // in ios9 array.subarray(from, undefined) will return empty array + // but array.subarray(from) or array.subarray(from, len) is correct + return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); + } - this._viewportCount = 1; + return array.slice( from, to ); - this._viewports = [ + }, - new Vector4( 0, 0, 1, 1 ) + // converts an array to a specific type + convertArray: function ( array, type, forceClone ) { - ]; + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - } + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - getViewportCount() { + return new type( array ); // create typed array - return this._viewportCount; + } - } + return Array.prototype.slice.call( array ); // create Array - getFrustum() { + }, - return this._frustum; + isTypedArray: function ( object ) { - } + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); - updateMatrices( light ) { + }, - const shadowCamera = this.camera; - const shadowMatrix = this.matrix; + // returns an array by which times and values can be sorted + getKeyframeOrder: function ( times ) { - _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld$1 ); + function compareTime( i, j ) { - _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _lookTarget$1 ); - shadowCamera.updateMatrixWorld(); + return times[ i ] - times[ j ]; - _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); + } - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); + const n = times.length; + const result = new Array( n ); + for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + result.sort( compareTime ); - } + return result; - getViewport( viewportIndex ) { + }, - return this._viewports[ viewportIndex ]; + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function ( values, stride, order ) { - } + const nValues = values.length; + const result = new values.constructor( nValues ); - getFrameExtents() { + for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - return this._frameExtents; + const srcOffset = order[ i ] * stride; - } + for ( let j = 0; j !== stride; ++ j ) { - dispose() { - - if ( this.map ) { + result[ dstOffset ++ ] = values[ srcOffset + j ]; - this.map.dispose(); + } } - if ( this.mapPass ) { + return result; - this.mapPass.dispose(); + }, - } + // function for parsing AOS keyframe formats + flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - } + let i = 1, key = jsonKeys[ 0 ]; - copy( source ) { + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - this.camera = source.camera.clone(); + key = jsonKeys[ i ++ ]; - this.bias = source.bias; - this.radius = source.radius; + } - this.mapSize.copy( source.mapSize ); + if ( key === undefined ) return; // no data - return this; + let value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - } + if ( Array.isArray( value ) ) { - clone() { + do { - return new this.constructor().copy( this ); + value = key[ valuePropertyName ]; - } + if ( value !== undefined ) { - toJSON() { + times.push( key.time ); + values.push.apply( values, value ); // push all elements - const object = {}; + } - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + key = jsonKeys[ i ++ ]; - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + } while ( key !== undefined ); - return object; + } else if ( value.toArray !== undefined ) { - } + // ...assume THREE.Math-ish -} + do { -class SpotLightShadow extends LightShadow { + value = key[ valuePropertyName ]; - constructor() { + if ( value !== undefined ) { - super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + times.push( key.time ); + value.toArray( values, values.length ); - this.focus = 1; + } - } + key = jsonKeys[ i ++ ]; - updateMatrices( light ) { + } while ( key !== undefined ); - const camera = this.camera; + } else { - const fov = RAD2DEG * 2 * light.angle * this.focus; - const aspect = this.mapSize.width / this.mapSize.height; - const far = light.distance || camera.far; + // otherwise push as-is - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + do { - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); + value = key[ valuePropertyName ]; - } + if ( value !== undefined ) { - super.updateMatrices( light ); + times.push( key.time ); + values.push( value ); - } + } - copy( source ) { + key = jsonKeys[ i ++ ]; - super.copy( source ); + } while ( key !== undefined ); - this.focus = source.focus; + } - return this; + }, - } + subclip: function ( sourceClip, name, startFrame, endFrame, fps = 30 ) { -} + const clip = sourceClip.clone(); -SpotLightShadow.prototype.isSpotLightShadow = true; + clip.name = name; -class SpotLight extends Light { + const tracks = []; - constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { + for ( let i = 0; i < clip.tracks.length; ++ i ) { - super( color, intensity ); + const track = clip.tracks[ i ]; + const valueSize = track.getValueSize(); - this.type = 'SpotLight'; + const times = []; + const values = []; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + for ( let j = 0; j < track.times.length; ++ j ) { - this.target = new Object3D(); + const frame = track.times[ j ] * fps; - this.distance = distance; - this.angle = angle; - this.penumbra = penumbra; - this.decay = decay; // for physically correct lights, should be 2. + if ( frame < startFrame || frame >= endFrame ) continue; - this.shadow = new SpotLightShadow(); + times.push( track.times[ j ] ); - } + for ( let k = 0; k < valueSize; ++ k ) { - get power() { + values.push( track.values[ j * valueSize + k ] ); - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; + } - } + } - set power( power ) { + if ( times.length === 0 ) continue; - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; + track.times = AnimationUtils.convertArray( times, track.times.constructor ); + track.values = AnimationUtils.convertArray( values, track.values.constructor ); - } + tracks.push( track ); - dispose() { + } - this.shadow.dispose(); + clip.tracks = tracks; - } + // find minimum .times value across all tracks in the trimmed clip - copy( source ) { + let minStartTime = Infinity; - super.copy( source ); + for ( let i = 0; i < clip.tracks.length; ++ i ) { - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - this.target = source.target.clone(); + minStartTime = clip.tracks[ i ].times[ 0 ]; - this.shadow = source.shadow.clone(); + } - return this; + } - } + // shift all tracks such that clip begins at t=0 -} + for ( let i = 0; i < clip.tracks.length; ++ i ) { -SpotLight.prototype.isSpotLight = true; + clip.tracks[ i ].shift( - 1 * minStartTime ); -const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); -const _lightPositionWorld = /*@__PURE__*/ new Vector3(); -const _lookTarget = /*@__PURE__*/ new Vector3(); + } -class PointLightShadow extends LightShadow { + clip.resetDuration(); - constructor() { + return clip; - super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + }, - this._frameExtents = new Vector2( 4, 2 ); + makeClipAdditive: function ( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { - this._viewportCount = 6; + if ( fps <= 0 ) fps = 30; - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction + const numTracks = referenceClip.tracks.length; + const referenceTime = referenceFrame / fps; - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; + // Make each track's values relative to the values at the reference frame + for ( let i = 0; i < numTracks; ++ i ) { - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; + const referenceTrack = referenceClip.tracks[ i ]; + const referenceTrackType = referenceTrack.ValueTypeName; - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; + // Skip this track if it's non-numeric + if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - } + // Find the track in the target clip whose name and type matches the reference track + const targetTrack = targetClip.tracks.find( function ( track ) { - updateMatrices( light, viewportIndex = 0 ) { + return track.name === referenceTrack.name + && track.ValueTypeName === referenceTrackType; - const camera = this.camera; - const shadowMatrix = this.matrix; + } ); - const far = light.distance || camera.far; + if ( targetTrack === undefined ) continue; - if ( far !== camera.far ) { + let referenceOffset = 0; + const referenceValueSize = referenceTrack.getValueSize(); - camera.far = far; - camera.updateProjectionMatrix(); + if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - } + referenceOffset = referenceValueSize / 3; - _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( _lightPositionWorld ); + } - _lookTarget.copy( camera.position ); - _lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( _lookTarget ); - camera.updateMatrixWorld(); + let targetOffset = 0; + const targetValueSize = targetTrack.getValueSize(); - shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); + if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix ); + targetOffset = targetValueSize / 3; - } + } -} + const lastIndex = referenceTrack.times.length - 1; + let referenceValue; -PointLightShadow.prototype.isPointLightShadow = true; + // Find the value to subtract out of the track + if ( referenceTime <= referenceTrack.times[ 0 ] ) { -class PointLight extends Light { + // Reference frame is earlier than the first keyframe, so just use the first keyframe + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - constructor( color, intensity, distance = 0, decay = 1 ) { + } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - super( color, intensity ); + // Reference frame is after the last keyframe, so just use the last keyframe + const startIndex = lastIndex * referenceValueSize + referenceOffset; + const endIndex = startIndex + referenceValueSize - referenceOffset; + referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex, endIndex ); - this.type = 'PointLight'; + } else { - this.distance = distance; - this.decay = decay; // for physically correct lights, should be 2. + // Interpolate to the reference value + const interpolant = referenceTrack.createInterpolant(); + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + interpolant.evaluate( referenceTime ); + referenceValue = AnimationUtils.arraySlice( interpolant.resultBuffer, startIndex, endIndex ); - this.shadow = new PointLightShadow(); + } - } + // Conjugate the quaternion + if ( referenceTrackType === 'quaternion' ) { - get power() { + const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); + referenceQuat.toArray( referenceValue ); - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; + } - } + // Subtract the reference value from all of the track values - set power( power ) { + const numTimes = targetTrack.times.length; + for ( let j = 0; j < numTimes; ++ j ) { - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); + const valueStart = j * targetValueSize + targetOffset; - } + if ( referenceTrackType === 'quaternion' ) { - dispose() { + // Multiply the conjugate for quaternion track types + Quaternion.multiplyQuaternionsFlat( + targetTrack.values, + valueStart, + referenceValue, + 0, + targetTrack.values, + valueStart + ); - this.shadow.dispose(); + } else { - } + const valueEnd = targetValueSize - targetOffset * 2; - copy( source ) { + // Subtract each value for all other numeric track types + for ( let k = 0; k < valueEnd; ++ k ) { - super.copy( source ); + targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - this.distance = source.distance; - this.decay = source.decay; + } - this.shadow = source.shadow.clone(); + } - return this; + } - } + } -} + targetClip.blendMode = AdditiveAnimationBlendMode; -PointLight.prototype.isPointLight = true; + return targetClip; -class OrthographicCamera extends Camera { + } - constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { +}; - super(); +/** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + */ - this.type = 'OrthographicCamera'; +class Interpolant { - this.zoom = 1; - this.view = null; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - this.near = near; - this.far = far; + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - this.updateProjectionMatrix(); + this.settings = null; + this.DefaultSettings_ = {}; } - copy( source, recursive ) { + evaluate( t ) { - super.copy( source, recursive ); + const pp = this.parameterPositions; + let i1 = this._cachedIndex, + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; + validate_interval: { - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); + seek: { - return this; + let right; - } + linear_scan: { - setViewOffset( fullWidth, fullHeight, x, y, width, height ) { + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - if ( this.view === null ) { + for ( let giveUpAt = i1 + 2; ; ) { - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; + if ( t1 === undefined ) { - } + if ( t < t0 ) break forward_scan; - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; + // after end - this.updateProjectionMatrix(); + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); - } + } - clearViewOffset() { + if ( i1 === giveUpAt ) break; // this loop - if ( this.view !== null ) { + t0 = t1; + t1 = pp[ ++ i1 ]; - this.view.enabled = false; + if ( t < t1 ) { - } + // we have arrived at the sought interval + break seek; - this.updateProjectionMatrix(); + } - } + } - updateProjectionMatrix() { + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; + } - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - if ( this.view !== null && this.view.enabled ) { + // looping? - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; + const t1global = pp[ 1 ]; - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; + if ( t < t1global ) { - } + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); + } - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); + // linear reverse scan - } + for ( let giveUpAt = i1 - 2; ; ) { - toJSON( meta ) { + if ( t0 === undefined ) { - const data = super.toJSON( meta ); + // before start - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + } - return data; + if ( i1 === giveUpAt ) break; // this loop - } + t1 = t0; + t0 = pp[ -- i1 - 1 ]; -} + if ( t >= t0 ) { -OrthographicCamera.prototype.isOrthographicCamera = true; + // we have arrived at the sought interval + break seek; -class DirectionalLightShadow extends LightShadow { + } - constructor() { + } - super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - } + } -} + // the interval is valid -DirectionalLightShadow.prototype.isDirectionalLightShadow = true; + break validate_interval; -class DirectionalLight extends Light { + } // linear scan - constructor( color, intensity ) { + // binary search - super( color, intensity ); + while ( i1 < right ) { - this.type = 'DirectionalLight'; + const mid = ( i1 + right ) >>> 1; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + if ( t < pp[ mid ] ) { - this.target = new Object3D(); + right = mid; - this.shadow = new DirectionalLightShadow(); + } else { - } + i1 = mid + 1; - dispose() { + } - this.shadow.dispose(); + } - } + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - copy( source ) { + // check boundary cases, again - super.copy( source ); + if ( t0 === undefined ) { - this.target = source.target.clone(); - this.shadow = source.shadow.clone(); + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - return this; + } - } + if ( t1 === undefined ) { -} + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); -DirectionalLight.prototype.isDirectionalLight = true; + } -class AmbientLight extends Light { + } // seek - constructor( color, intensity ) { + this._cachedIndex = i1; - super( color, intensity ); + this.intervalChanged_( i1, t0, t1 ); - this.type = 'AmbientLight'; + } // validate_interval + + return this.interpolate_( i1, t0, t, t1 ); } -} + getSettings_() { -AmbientLight.prototype.isAmbientLight = true; + return this.settings || this.DefaultSettings_; -class RectAreaLight extends Light { + } - constructor( color, intensity, width = 10, height = 10 ) { + copySampleValue_( index ) { - super( color, intensity ); + // copies a sample value to the result buffer - this.type = 'RectAreaLight'; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - this.width = width; - this.height = height; + for ( let i = 0; i !== stride; ++ i ) { - } + result[ i ] = values[ offset + i ]; - copy( source ) { + } - super.copy( source ); + return result; - this.width = source.width; - this.height = source.height; + } - return this; + // Template methods for derived classes: - } + interpolate_( /* i1, t0, t, t1 */ ) { - toJSON( meta ) { + throw new Error( 'call to abstract method' ); + // implementations shall return this.resultBuffer - const data = super.toJSON( meta ); + } - data.object.width = this.width; - data.object.height = this.height; + intervalChanged_( /* i1, t0, t1 */ ) { - return data; + // empty } } -RectAreaLight.prototype.isRectAreaLight = true; +// ALIAS DEFINITIONS + +Interpolant.prototype.beforeStart_ = Interpolant.prototype.copySampleValue_; +Interpolant.prototype.afterEnd_ = Interpolant.prototype.copySampleValue_; /** - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * Fast and simple cubic spline interpolant. * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. */ -// 3-band SH defined by 9 coefficients +class CubicInterpolant extends Interpolant { -class SphericalHarmonics3 { + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - constructor() { + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - this.coefficients = []; + this._weightPrev = - 0; + this._offsetPrev = - 0; + this._weightNext = - 0; + this._offsetNext = - 0; - for ( let i = 0; i < 9; i ++ ) { + this.DefaultSettings_ = { - this.coefficients.push( new Vector3() ); + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding - } + }; } - set( coefficients ) { + intervalChanged_( i1, t0, t1 ) { - for ( let i = 0; i < 9; i ++ ) { + const pp = this.parameterPositions; + let iPrev = i1 - 2, + iNext = i1 + 1, - this.coefficients[ i ].copy( coefficients[ i ] ); + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; - } + if ( tPrev === undefined ) { - return this; + switch ( this.getSettings_().endingStart ) { - } + case ZeroSlopeEnding: - zero() { + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; - for ( let i = 0; i < 9; i ++ ) { + break; - this.coefficients[ i ].set( 0, 0, 0 ); + case WrapAroundEnding: - } + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - return this; + break; - } + default: // ZeroCurvatureEnding - // get the radiance in the direction of the normal - // target is a Vector3 - getAt( normal, target ) { + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; - // normal is assumed to be unit length + } - const x = normal.x, y = normal.y, z = normal.z; + } - const coeff = this.coefficients; + if ( tNext === undefined ) { - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); + switch ( this.getSettings_().endingEnd ) { - // band 1 - target.addScaledVector( coeff[ 1 ], 0.488603 * y ); - target.addScaledVector( coeff[ 2 ], 0.488603 * z ); - target.addScaledVector( coeff[ 3 ], 0.488603 * x ); + case ZeroSlopeEnding: - // band 2 - target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; - return target; + break; - } + case WrapAroundEnding: - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt( normal, target ) { + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; - // normal is assumed to be unit length + break; - const x = normal.x, y = normal.y, z = normal.z; + default: // ZeroCurvatureEnding - const coeff = this.coefficients; + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 + } - // band 1 - target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); + } - // band 2 - target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 + const halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; - return target; + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; } - add( sh ) { + interpolate_( i1, t0, t, t1 ) { - for ( let i = 0; i < 9; i ++ ) { + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - this.coefficients[ i ].add( sh.coefficients[ i ] ); + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, - } + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; - return this; + // evaluate polynomials - } + const sP = - wP * ppp + 2 * wP * pp - wP * p; + const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; + const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + const sN = wN * ppp - wN * pp; - addScaledSH( sh, s ) { + // combine data linearly - for ( let i = 0; i < 9; i ++ ) { + for ( let i = 0; i !== stride; ++ i ) { - this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; } - return this; + return result; } - scale( s ) { - - for ( let i = 0; i < 9; i ++ ) { +} - this.coefficients[ i ].multiplyScalar( s ); +class LinearInterpolant extends Interpolant { - } + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - return this; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } - lerp( sh, alpha ) { - - for ( let i = 0; i < 9; i ++ ) { - - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); + interpolate_( i1, t0, t, t1 ) { - } + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - return this; + offset1 = i1 * stride, + offset0 = offset1 - stride, - } + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - equals( sh ) { + for ( let i = 0; i !== stride; ++ i ) { - for ( let i = 0; i < 9; i ++ ) { + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { + } - return false; + return result; - } + } - } +} - return true; +/** + * + * Interpolant that evaluates to the sample value at the position preceding + * the parameter. + */ - } +class DiscreteInterpolant extends Interpolant { - copy( sh ) { + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - return this.set( sh.coefficients ); + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); } - clone() { + interpolate_( i1 /*, t0, t, t1 */ ) { - return new this.constructor().copy( this ); + return this.copySampleValue_( i1 - 1 ); } - fromArray( array, offset = 0 ) { +} - const coefficients = this.coefficients; +class KeyframeTrack { - for ( let i = 0; i < 9; i ++ ) { + constructor( name, times, values, interpolation ) { - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); + if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); + if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - } + this.name = name; - return this; + this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); + + this.setInterpolation( interpolation || this.DefaultInterpolation ); } - toArray( array = [], offset = 0 ) { + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): - const coefficients = this.coefficients; + static toJSON( track ) { - for ( let i = 0; i < 9; i ++ ) { + const trackType = track.constructor; - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); + let json; - } + // derived classes can define a static toJSON method + if ( trackType.toJSON !== this.toJSON ) { - return array; + json = trackType.toJSON( track ); - } + } else { - // evaluate the basis functions - // shBasis is an Array[ 9 ] - static getBasisAt( normal, shBasis ) { + // by default, we assume the data can be serialized as-is + json = { - // normal is assumed to be unit length + 'name': track.name, + 'times': AnimationUtils.convertArray( track.times, Array ), + 'values': AnimationUtils.convertArray( track.values, Array ) - const x = normal.x, y = normal.y, z = normal.z; + }; - // band 0 - shBasis[ 0 ] = 0.282095; + const interpolation = track.getInterpolation(); - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; + if ( interpolation !== track.DefaultInterpolation ) { - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); + json.interpolation = interpolation; - } + } -} + } -SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; + json.type = track.ValueTypeName; // mandatory -class LightProbe extends Light { + return json; - constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { + } - super( undefined, intensity ); + InterpolantFactoryMethodDiscrete( result ) { - this.sh = sh; + return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); } - copy( source ) { - - super.copy( source ); - - this.sh.copy( source.sh ); + InterpolantFactoryMethodLinear( result ) { - return this; + return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); } - fromJSON( json ) { - - this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); - this.sh.fromArray( json.sh ); + InterpolantFactoryMethodSmooth( result ) { - return this; + return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); } - toJSON( meta ) { + setInterpolation( interpolation ) { - const data = super.toJSON( meta ); + let factoryMethod; - data.object.sh = this.sh.toArray(); + switch ( interpolation ) { - return data; + case InterpolateDiscrete: - } + factoryMethod = this.InterpolantFactoryMethodDiscrete; -} + break; -LightProbe.prototype.isLightProbe = true; + case InterpolateLinear: -class LoaderUtils { + factoryMethod = this.InterpolantFactoryMethodLinear; - static decodeText( array ) { + break; - if ( typeof TextDecoder !== 'undefined' ) { + case InterpolateSmooth: - return new TextDecoder().decode( array ); + factoryMethod = this.InterpolantFactoryMethodSmooth; + + break; } - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. + if ( factoryMethod === undefined ) { - let s = ''; + const message = 'unsupported interpolation for ' + + this.ValueTypeName + ' keyframe track named ' + this.name; - for ( let i = 0, il = array.length; i < il; i ++ ) { + if ( this.createInterpolant === undefined ) { - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { - } + this.setInterpolation( this.DefaultInterpolation ); - try { + } else { - // merges multi-byte utf-8 characters. + throw new Error( message ); // fatal, in this case - return decodeURIComponent( escape( s ) ); + } - } catch ( e ) { // see #16358 + } - return s; + console.warn( 'THREE.KeyframeTrack:', message ); + return this; } - } - - static extractUrlBase( url ) { + this.createInterpolant = factoryMethod; - const index = url.lastIndexOf( '/' ); + return this; - if ( index === - 1 ) return './'; + } - return url.substr( 0, index + 1 ); + getInterpolation() { - } + switch ( this.createInterpolant ) { -} + case this.InterpolantFactoryMethodDiscrete: -class InstancedBufferGeometry extends BufferGeometry { + return InterpolateDiscrete; - constructor() { + case this.InterpolantFactoryMethodLinear: - super(); + return InterpolateLinear; - this.type = 'InstancedBufferGeometry'; - this.instanceCount = Infinity; + case this.InterpolantFactoryMethodSmooth: - } + return InterpolateSmooth; - copy( source ) { + } - super.copy( source ); + } - this.instanceCount = source.instanceCount; + getValueSize() { - return this; + return this.values.length / this.times.length; } - clone() { + // move all keyframes either forwards or backwards in time + shift( timeOffset ) { - return new this.constructor().copy( this ); + if ( timeOffset !== 0.0 ) { - } + const times = this.times; - toJSON() { + for ( let i = 0, n = times.length; i !== n; ++ i ) { - const data = super.toJSON( this ); + times[ i ] += timeOffset; - data.instanceCount = this.instanceCount; + } - data.isInstancedBufferGeometry = true; + } - return data; + return this; } -} + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale( timeScale ) { -InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; - -class InstancedBufferAttribute extends BufferAttribute { - - constructor( array, itemSize, normalized, meshPerAttribute ) { + if ( timeScale !== 1.0 ) { - if ( typeof ( normalized ) === 'number' ) { + const times = this.times; - meshPerAttribute = normalized; + for ( let i = 0, n = times.length; i !== n; ++ i ) { - normalized = false; + times[ i ] *= timeScale; - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); + } } - super( array, itemSize, normalized ); - - this.meshPerAttribute = meshPerAttribute || 1; - - } - - copy( source ) { - - super.copy( source ); - - this.meshPerAttribute = source.meshPerAttribute; - return this; } - toJSON() { - - const data = super.toJSON(); + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim( startTime, endTime ) { - data.meshPerAttribute = this.meshPerAttribute; + const times = this.times, + nKeys = times.length; - data.isInstancedBufferAttribute = true; + let from = 0, + to = nKeys - 1; - return data; + while ( from !== nKeys && times[ from ] < startTime ) { - } + ++ from; -} + } -InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; + while ( to !== - 1 && times[ to ] > endTime ) { -class ImageBitmapLoader extends Loader { + -- to; - constructor( manager ) { + } - super( manager ); + ++ to; // inclusive -> exclusive bound - if ( typeof createImageBitmap === 'undefined' ) { + if ( from !== 0 || to !== nKeys ) { - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) { - } + to = Math.max( to, 1 ); + from = to - 1; - if ( typeof fetch === 'undefined' ) { + } - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + const stride = this.getValueSize(); + this.times = AnimationUtils.arraySlice( times, from, to ); + this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); } - this.options = { premultiplyAlpha: 'none' }; + return this; } - setOptions( options ) { + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate() { - this.options = options; + let valid = true; - return this; + const valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - } + console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); + valid = false; - load( url, onLoad, onProgress, onError ) { + } - if ( url === undefined ) url = ''; + const times = this.times, + values = this.values, - if ( this.path !== undefined ) url = this.path + url; + nKeys = times.length; - url = this.manager.resolveURL( url ); + if ( nKeys === 0 ) { - const scope = this; + console.error( 'THREE.KeyframeTrack: Track is empty.', this ); + valid = false; - const cached = Cache.get( url ); + } - if ( cached !== undefined ) { + let prevTime = null; - scope.manager.itemStart( url ); + for ( let i = 0; i !== nKeys; i ++ ) { - setTimeout( function () { + const currTime = times[ i ]; - if ( onLoad ) onLoad( cached ); + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - scope.manager.itemEnd( url ); + console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); + valid = false; + break; - }, 0 ); + } - return cached; + if ( prevTime !== null && prevTime > currTime ) { - } + console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); + valid = false; + break; - const fetchOptions = {}; - fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; - fetchOptions.headers = this.requestHeader; + } - fetch( url, fetchOptions ).then( function ( res ) { + prevTime = currTime; - return res.blob(); + } - } ).then( function ( blob ) { + if ( values !== undefined ) { - return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); + if ( AnimationUtils.isTypedArray( values ) ) { - } ).then( function ( imageBitmap ) { + for ( let i = 0, n = values.length; i !== n; ++ i ) { - Cache.add( url, imageBitmap ); + const value = values[ i ]; - if ( onLoad ) onLoad( imageBitmap ); + if ( isNaN( value ) ) { - scope.manager.itemEnd( url ); + console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); + valid = false; + break; - } ).catch( function ( e ) { + } - if ( onError ) onError( e ); + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - } ); + } - scope.manager.itemStart( url ); + return valid; } -} + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize() { -ImageBitmapLoader.prototype.isImageBitmapLoader = true; + // times or values may be shared with other tracks, so overwriting is unsafe + const times = AnimationUtils.arraySlice( this.times ), + values = AnimationUtils.arraySlice( this.values ), + stride = this.getValueSize(), -let _context; + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, -const AudioContext = { + lastIndex = times.length - 1; - getContext: function () { + let writeIndex = 1; - if ( _context === undefined ) { + for ( let i = 1; i < lastIndex; ++ i ) { - _context = new ( window.AudioContext || window.webkitAudioContext )(); + let keep = false; - } + const time = times[ i ]; + const timeNext = times[ i + 1 ]; - return _context; + // remove adjacent keyframes scheduled at the same time - }, + if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { - setContext: function ( value ) { + if ( ! smoothInterpolation ) { - _context = value; + // remove unnecessary keyframes same as their neighbors - } + const offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; -}; + for ( let j = 0; j !== stride; ++ j ) { -class AudioLoader extends Loader { + const value = values[ offset + j ]; - constructor( manager ) { + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - super( manager ); + keep = true; + break; - } + } - load( url, onLoad, onProgress, onError ) { + } - const scope = this; + } else { - const loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( buffer ) { + keep = true; - try { + } - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - const bufferCopy = buffer.slice( 0 ); + } - const context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { + // in-place compaction - onLoad( audioBuffer ); + if ( keep ) { - } ); + if ( i !== writeIndex ) { - } catch ( e ) { + times[ writeIndex ] = times[ i ]; - if ( onError ) { + const readOffset = i * stride, + writeOffset = writeIndex * stride; - onError( e ); + for ( let j = 0; j !== stride; ++ j ) { - } else { + values[ writeOffset + j ] = values[ readOffset + j ]; - console.error( e ); + } } - scope.manager.itemError( url ); + ++ writeIndex; } - }, onProgress, onError ); + } - } + // flush last keyframe (compaction looks ahead) -} + if ( lastIndex > 0 ) { -class HemisphereLightProbe extends LightProbe { + times[ writeIndex ] = times[ lastIndex ]; - constructor( skyColor, groundColor, intensity = 1 ) { + for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - super( undefined, intensity ); + values[ writeOffset + j ] = values[ readOffset + j ]; - const color1 = new Color().set( skyColor ); - const color2 = new Color().set( groundColor ); + } - const sky = new Vector3( color1.r, color1.g, color1.b ); - const ground = new Vector3( color2.r, color2.g, color2.b ); + ++ writeIndex; - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - const c0 = Math.sqrt( Math.PI ); - const c1 = c0 * Math.sqrt( 0.75 ); + } - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); + if ( writeIndex !== times.length ) { - } + this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); -} + } else { -HemisphereLightProbe.prototype.isHemisphereLightProbe = true; + this.times = times; + this.values = values; -class AmbientLightProbe extends LightProbe { + } - constructor( color, intensity = 1 ) { + return this; - super( undefined, intensity ); + } - const color1 = new Color().set( color ); + clone() { - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); + const times = AnimationUtils.arraySlice( this.times, 0 ); + const values = AnimationUtils.arraySlice( this.values, 0 ); - } + const TypedKeyframeTrack = this.constructor; + const track = new TypedKeyframeTrack( this.name, times, values ); -} + // Interpolant argument to constructor is not saved, so copy the factory method directly. + track.createInterpolant = this.createInterpolant; -AmbientLightProbe.prototype.isAmbientLightProbe = true; + return track; -class Clock { + } - constructor( autoStart = true ) { +} - this.autoStart = autoStart; +KeyframeTrack.prototype.TimeBufferType = Float32Array; +KeyframeTrack.prototype.ValueBufferType = Float32Array; +KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; +/** + * A Track of Boolean keyframe values. + */ +class BooleanKeyframeTrack extends KeyframeTrack {} - this.running = false; +BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; +BooleanKeyframeTrack.prototype.ValueBufferType = Array; +BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; +BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; +BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - } +/** + * A Track of keyframe values that represent color. + */ +class ColorKeyframeTrack extends KeyframeTrack {} - start() { +ColorKeyframeTrack.prototype.ValueTypeName = 'color'; - this.startTime = now(); +/** + * A Track of numeric keyframe values. + */ +class NumberKeyframeTrack extends KeyframeTrack {} - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; +NumberKeyframeTrack.prototype.ValueTypeName = 'number'; - } +/** + * Spherical linear unit quaternion interpolant. + */ - stop() { +class QuaternionLinearInterpolant extends Interpolant { - this.getElapsedTime(); - this.running = false; - this.autoStart = false; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - } + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - getElapsedTime() { + } - this.getDelta(); - return this.elapsedTime; + interpolate_( i1, t0, t, t1 ) { - } + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - getDelta() { + alpha = ( t - t0 ) / ( t1 - t0 ); - let diff = 0; + let offset = i1 * stride; - if ( this.autoStart && ! this.running ) { + for ( let end = offset + stride; offset !== end; offset += 4 ) { - this.start(); - return 0; + Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } - if ( this.running ) { + return result; - const newTime = now(); + } - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; +} - this.elapsedTime += diff; +/** + * A Track of quaternion keyframe values. + */ +class QuaternionKeyframeTrack extends KeyframeTrack { - } + InterpolantFactoryMethodLinear( result ) { - return diff; + return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); } } -function now() { - - return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 +QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; +// ValueBufferType is inherited +QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; +QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; -} +/** + * A Track that interpolates Strings + */ +class StringKeyframeTrack extends KeyframeTrack {} -class Audio extends Object3D { +StringKeyframeTrack.prototype.ValueTypeName = 'string'; +StringKeyframeTrack.prototype.ValueBufferType = Array; +StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; +StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; +StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - constructor( listener ) { +/** + * A Track of vectored keyframe values. + */ +class VectorKeyframeTrack extends KeyframeTrack {} - super(); +VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; - this.type = 'Audio'; +class AnimationClip { - this.listener = listener; - this.context = listener.context; + constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); + this.name = name; + this.tracks = tracks; + this.duration = duration; + this.blendMode = blendMode; - this.autoplay = false; + this.uuid = generateUUID(); - this.buffer = null; - this.detune = 0; - this.loop = false; - this.loopStart = 0; - this.loopEnd = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.source = null; - this.sourceType = 'empty'; + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - this._startedAt = 0; - this._progress = 0; - this._connected = false; + this.resetDuration(); - this.filters = []; + } } - getOutput() { - return this.gain; + static parse( json ) { - } + const tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - setNodeSource( audioNode ) { + for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); + tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - return this; + } + + const clip = new this( json.name, json.duration, tracks, json.blendMode ); + clip.uuid = json.uuid; + + return clip; } - setMediaElementSource( mediaElement ) { + static toJSON( clip ) { - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); + const tracks = [], + clipTracks = clip.tracks; - return this; + const json = { - } + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid, + 'blendMode': clip.blendMode - setMediaStreamSource( mediaStream ) { + }; - this.hasPlaybackControl = false; - this.sourceType = 'mediaStreamNode'; - this.source = this.context.createMediaStreamSource( mediaStream ); - this.connect(); + for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - return this; + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + + } + + return json; } - setBuffer( audioBuffer ) { + static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { - this.buffer = audioBuffer; - this.sourceType = 'buffer'; + const numMorphTargets = morphTargetSequence.length; + const tracks = []; - if ( this.autoplay ) this.play(); + for ( let i = 0; i < numMorphTargets; i ++ ) { - return this; + let times = []; + let values = []; - } + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - play( delay = 0 ) { + values.push( 0, 1, 0 ); - if ( this.isPlaying === true ) { + const order = AnimationUtils.getKeyframeOrder( times ); + times = AnimationUtils.sortedArray( times, 1, order ); + values = AnimationUtils.sortedArray( values, 1, order ); - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( ! noLoop && times[ 0 ] === 0 ) { - } + times.push( numMorphTargets ); + values.push( values[ 0 ] ); - if ( this.hasPlaybackControl === false ) { + } - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); } - this._startedAt = this.context.currentTime + delay; + return new this( name, - 1, tracks ); - const source = this.context.createBufferSource(); - source.buffer = this.buffer; - source.loop = this.loop; - source.loopStart = this.loopStart; - source.loopEnd = this.loopEnd; - source.onended = this.onEnded.bind( this ); - source.start( this._startedAt, this._progress + this.offset, this.duration ); + } - this.isPlaying = true; + static findByName( objectOrClipArray, name ) { - this.source = source; + let clipArray = objectOrClipArray; - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); + if ( ! Array.isArray( objectOrClipArray ) ) { - return this.connect(); + const o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; - } + } - pause() { + for ( let i = 0; i < clipArray.length; i ++ ) { - if ( this.hasPlaybackControl === false ) { + if ( clipArray[ i ].name === name ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + return clipArray[ i ]; + + } } - if ( this.isPlaying === true ) { + return null; - // update current progress + } - this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; + static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - if ( this.loop === true ) { + const animationToMorphTargets = {}; - // ensure _progress does not exceed duration with looped audios + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + const pattern = /^([\w-]*?)([\d]+)$/; - this._progress = this._progress % ( this.duration || this.buffer.duration ); + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - } + const morphTarget = morphTargets[ i ]; + const parts = morphTarget.name.match( pattern ); - this.source.stop(); - this.source.onended = null; + if ( parts && parts.length > 1 ) { - this.isPlaying = false; + const name = parts[ 1 ]; - } + let animationMorphTargets = animationToMorphTargets[ name ]; - return this; + if ( ! animationMorphTargets ) { - } + animationToMorphTargets[ name ] = animationMorphTargets = []; - stop() { + } - if ( this.hasPlaybackControl === false ) { + animationMorphTargets.push( morphTarget ); - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + } } - this._progress = 0; + const clips = []; - this.source.stop(); - this.source.onended = null; - this.isPlaying = false; + for ( const name in animationToMorphTargets ) { - return this; + clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - } + } - connect() { + return clips; - if ( this.filters.length > 0 ) { + } - this.source.connect( this.filters[ 0 ] ); + // parse the animation.hierarchy format + static parseAnimation( animation, bones ) { - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { + if ( ! animation ) { - this.filters[ i - 1 ].connect( this.filters[ i ] ); + console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); + return null; - } + } - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); + const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - } else { + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - this.source.connect( this.getOutput() ); + const times = []; + const values = []; - } + AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - this._connected = true; + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - return this; + destTracks.push( new trackType( trackName, times, values ) ); - } + } - disconnect() { + } - if ( this.filters.length > 0 ) { + }; - this.source.disconnect( this.filters[ 0 ] ); + const tracks = []; - for ( let i = 1, l = this.filters.length; i < l; i ++ ) { + const clipName = animation.name || 'default'; + const fps = animation.fps || 30; + const blendMode = animation.blendMode; - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); + // automatic length determination in AnimationClip. + let duration = animation.length || - 1; - } + const hierarchyTracks = animation.hierarchy || []; - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); + for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - } else { + const animationKeys = hierarchyTracks[ h ].keys; - this.source.disconnect( this.getOutput() ); + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - } + // process morph targets + if ( animationKeys[ 0 ].morphTargets ) { - this._connected = false; + // figure out all morph targets used in this track + const morphTargetNames = {}; - return this; + let k; - } + for ( k = 0; k < animationKeys.length; k ++ ) { - getFilters() { + if ( animationKeys[ k ].morphTargets ) { - return this.filters; + for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - } + morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - setFilters( value ) { + } - if ( ! value ) value = []; + } - if ( this._connected === true ) { + } - this.disconnect(); - this.filters = value.slice(); - this.connect(); + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( const morphTargetName in morphTargetNames ) { - } else { + const times = []; + const values = []; - this.filters = value.slice(); + for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - } + const animationKey = animationKeys[ k ]; - return this; + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - } + } - setDetune( value ) { + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - this.detune = value; + } - if ( this.source.detune === undefined ) return; // only set detune when available + duration = morphTargetNames.length * fps; - if ( this.isPlaying === true ) { + } else { - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); + // ...assume skeletal animation - } + const boneName = '.bones[' + bones[ h ].name + ']'; - return this; + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); - } + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); - getDetune() { + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); - return this.detune; + } - } + } - getFilter() { + if ( tracks.length === 0 ) { - return this.getFilters()[ 0 ]; + return null; - } + } - setFilter( filter ) { + const clip = new this( clipName, duration, tracks, blendMode ); - return this.setFilters( filter ? [ filter ] : [] ); + return clip; } - setPlaybackRate( value ) { - - if ( this.hasPlaybackControl === false ) { - - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + resetDuration() { - } + const tracks = this.tracks; + let duration = 0; - this.playbackRate = value; + for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - if ( this.isPlaying === true ) { + const track = this.tracks[ i ]; - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); + duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } + this.duration = duration; + return this; } - getPlaybackRate() { + trim() { - return this.playbackRate; + for ( let i = 0; i < this.tracks.length; i ++ ) { - } + this.tracks[ i ].trim( 0, this.duration ); - onEnded() { + } - this.isPlaying = false; + return this; } - getLoop() { + validate() { - if ( this.hasPlaybackControl === false ) { + let valid = true; - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; + for ( let i = 0; i < this.tracks.length; i ++ ) { + + valid = valid && this.tracks[ i ].validate(); } - return this.loop; + return valid; } - setLoop( value ) { + optimize() { - if ( this.hasPlaybackControl === false ) { + for ( let i = 0; i < this.tracks.length; i ++ ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + this.tracks[ i ].optimize(); } - this.loop = value; - - if ( this.isPlaying === true ) { + return this; - this.source.loop = this.loop; + } - } + clone() { - return this; + const tracks = []; - } + for ( let i = 0; i < this.tracks.length; i ++ ) { - setLoopStart( value ) { + tracks.push( this.tracks[ i ].clone() ); - this.loopStart = value; + } - return this; + return new this.constructor( this.name, this.duration, tracks, this.blendMode ); } - setLoopEnd( value ) { - - this.loopEnd = value; + toJSON() { - return this; + return this.constructor.toJSON( this ); } - getVolume() { +} - return this.gain.gain.value; +function getTrackTypeForValueTypeName( typeName ) { - } + switch ( typeName.toLowerCase() ) { - setVolume( value ) { + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); + return NumberKeyframeTrack; - return this; + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': - } + return VectorKeyframeTrack; -} + case 'color': -class PropertyMixer { + return ColorKeyframeTrack; - constructor( binding, typeName, valueSize ) { + case 'quaternion': - this.binding = binding; - this.valueSize = valueSize; + return QuaternionKeyframeTrack; - let mixFunction, - mixFunctionAdditive, - setIdentity; + case 'bool': + case 'boolean': - // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property - // - // 'add' is used for additive cumulative results - // - // 'work' is optional and is only present for quaternion types. It is used - // to store intermediate quaternion multiplication results + return BooleanKeyframeTrack; - switch ( typeName ) { + case 'string': - case 'quaternion': - mixFunction = this._slerp; - mixFunctionAdditive = this._slerpAdditive; - setIdentity = this._setAdditiveIdentityQuaternion; + return StringKeyframeTrack; - this.buffer = new Float64Array( valueSize * 6 ); - this._workIndex = 5; - break; + } - case 'string': - case 'bool': - mixFunction = this._select; + throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - // Use the regular mix function and for additive on these types, - // additive is not relevant for non-numeric types - mixFunctionAdditive = this._select; +} - setIdentity = this._setAdditiveIdentityOther; +function parseKeyframeTrack( json ) { - this.buffer = new Array( valueSize * 5 ); - break; + if ( json.type === undefined ) { - default: - mixFunction = this._lerp; - mixFunctionAdditive = this._lerpAdditive; - setIdentity = this._setAdditiveIdentityNumeric; + throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - this.buffer = new Float64Array( valueSize * 5 ); + } - } + const trackType = getTrackTypeForValueTypeName( json.type ); - this._mixBufferRegion = mixFunction; - this._mixBufferRegionAdditive = mixFunctionAdditive; - this._setIdentity = setIdentity; - this._origIndex = 3; - this._addIndex = 4; + if ( json.times === undefined ) { - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + const times = [], values = []; - this.useCount = 0; - this.referenceCount = 0; + AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + + json.times = times; + json.values = values; } - // accumulate data in the 'incoming' region into 'accu' - accumulate( accuIndex, weight ) { + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place + return trackType.parse( json ); - const buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride; + } else { - let currentWeight = this.cumulativeWeight; + // by default, we assume a constructor compatible with the base + return new trackType( json.name, json.times, json.values, json.interpolation ); - if ( currentWeight === 0 ) { + } - // accuN := incoming * weight +} - for ( let i = 0; i !== stride; ++ i ) { +const Cache = { - buffer[ offset + i ] = buffer[ i ]; + enabled: false, - } + files: {}, - currentWeight = weight; + add: function ( key, file ) { - } else { + if ( this.enabled === false ) return; - // accuN := accuN + incoming * weight + // console.log( 'THREE.Cache', 'Adding key:', key ); - currentWeight += weight; - const mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); + this.files[ key ] = file; - } + }, - this.cumulativeWeight = currentWeight; + get: function ( key ) { - } + if ( this.enabled === false ) return; - // accumulate data in the 'incoming' region into 'add' - accumulateAdditive( weight ) { + // console.log( 'THREE.Cache', 'Checking key:', key ); - const buffer = this.buffer, - stride = this.valueSize, - offset = stride * this._addIndex; + return this.files[ key ]; - if ( this.cumulativeWeightAdditive === 0 ) { + }, - // add = identity + remove: function ( key ) { - this._setIdentity(); + delete this.files[ key ]; - } + }, - // add := add + incoming * weight + clear: function () { - this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); - this.cumulativeWeightAdditive += weight; + this.files = {}; } - // apply the state of 'accu' to the binding when accus differ - apply( accuIndex ) { +}; - const stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, +class LoadingManager { - weight = this.cumulativeWeight, - weightAdditive = this.cumulativeWeightAdditive, + constructor( onLoad, onProgress, onError ) { - binding = this.binding; + const scope = this; - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + let isLoading = false; + let itemsLoaded = 0; + let itemsTotal = 0; + let urlModifier = undefined; + const handlers = []; - if ( weight < 1 ) { + // Refer to #5689 for the reason why we don't set .onStart + // in the constructor - // accuN := accuN + original * ( 1 - cumulativeWeight ) + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - const originalValueOffset = stride * this._origIndex; + this.itemStart = function ( url ) { - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); + itemsTotal ++; - } + if ( isLoading === false ) { - if ( weightAdditive > 0 ) { + if ( scope.onStart !== undefined ) { - // accuN := accuN + additive accuN + scope.onStart( url, itemsLoaded, itemsTotal ); - this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); + } - } + } - for ( let i = stride, e = stride + stride; i !== e; ++ i ) { + isLoading = true; - if ( buffer[ i ] !== buffer[ i + stride ] ) { + }; - // value has changed -> update scene graph + this.itemEnd = function ( url ) { - binding.setValue( buffer, offset ); - break; + itemsLoaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, itemsLoaded, itemsTotal ); } - } + if ( itemsLoaded === itemsTotal ) { - } + isLoading = false; - // remember the state of the bound property and copy it to both accus - saveOriginalState() { + if ( scope.onLoad !== undefined ) { - const binding = this.binding; + scope.onLoad(); - const buffer = this.buffer, - stride = this.valueSize, + } - originalValueOffset = stride * this._origIndex; + } - binding.getValue( buffer, originalValueOffset ); + }; - // accu[0..1] := orig -- initially detect changes against the original - for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { + this.itemError = function ( url ) { - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; + if ( scope.onError !== undefined ) { - } + scope.onError( url ); - // Add to identity for additive - this._setIdentity(); + } - this.cumulativeWeight = 0; - this.cumulativeWeightAdditive = 0; + }; - } + this.resolveURL = function ( url ) { - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState() { + if ( urlModifier ) { - const originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); + return urlModifier( url ); - } + } - _setAdditiveIdentityNumeric() { + return url; - const startIndex = this._addIndex * this.valueSize; - const endIndex = startIndex + this.valueSize; + }; - for ( let i = startIndex; i < endIndex; i ++ ) { + this.setURLModifier = function ( transform ) { - this.buffer[ i ] = 0; + urlModifier = transform; - } + return this; - } + }; - _setAdditiveIdentityQuaternion() { + this.addHandler = function ( regex, loader ) { - this._setAdditiveIdentityNumeric(); - this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; + handlers.push( regex, loader ); - } + return this; - _setAdditiveIdentityOther() { + }; - const startIndex = this._origIndex * this.valueSize; - const targetIndex = this._addIndex * this.valueSize; + this.removeHandler = function ( regex ) { - for ( let i = 0; i < this.valueSize; i ++ ) { + const index = handlers.indexOf( regex ); - this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; + if ( index !== - 1 ) { - } + handlers.splice( index, 2 ); - } + } + return this; - // mix functions + }; - _select( buffer, dstOffset, srcOffset, t, stride ) { + this.getHandler = function ( file ) { - if ( t >= 0.5 ) { + for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - for ( let i = 0; i !== stride; ++ i ) { + const regex = handlers[ i ]; + const loader = handlers[ i + 1 ]; - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; + if ( regex.global ) regex.lastIndex = 0; // see #17920 - } + if ( regex.test( file ) ) { - } + return loader; - } + } - _slerp( buffer, dstOffset, srcOffset, t ) { + } - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); + return null; + + }; } - _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { +} - const workOffset = this._workIndex * stride; +const DefaultLoadingManager = new LoadingManager(); - // Store result in intermediate buffer offset - Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); +class Loader { - // Slerp to the intermediate result - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); + constructor( manager ) { - } + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - _lerp( buffer, dstOffset, srcOffset, t, stride ) { + this.crossOrigin = 'anonymous'; + this.withCredentials = false; + this.path = ''; + this.resourcePath = ''; + this.requestHeader = {}; - const s = 1 - t; + } - for ( let i = 0; i !== stride; ++ i ) { + load( /* url, onLoad, onProgress, onError */ ) {} - const j = dstOffset + i; + loadAsync( url, onProgress ) { - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; + const scope = this; - } + return new Promise( function ( resolve, reject ) { - } + scope.load( url, resolve, onProgress, reject ); - _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { + } ); - for ( let i = 0; i !== stride; ++ i ) { + } - const j = dstOffset + i; + parse( /* data */ ) {} - buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; + setCrossOrigin( crossOrigin ) { - } + this.crossOrigin = crossOrigin; + return this; } -} - -// Characters [].:/ are reserved for track binding syntax. -const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; -const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - -// Attempts to allow node names from any language. ES5's `\w` regexp matches -// only latin characters, and the unicode \p{L} is not yet supported. So -// instead, we exclude reserved characters and match everything else. -const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; -const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; + setWithCredentials( value ) { -// Parent directories, delimited by '/' or ':'. Currently unused, but must -// be matched to parse the rest of the track name. -const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); + this.withCredentials = value; + return this; -// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. -const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); + } -// Object on target node, and accessor. May not contain reserved -// characters. Accessor may contain any character except closing bracket. -const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); + setPath( path ) { -// Property and accessor. May not contain reserved characters. Accessor may -// contain any non-bracket characters. -const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); + this.path = path; + return this; -const _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' -); + } -const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; + setResourcePath( resourcePath ) { -class Composite { + this.resourcePath = resourcePath; + return this; - constructor( targetGroup, path, optionalParsedPath ) { + } - const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); + setRequestHeader( requestHeader ) { - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); + this.requestHeader = requestHeader; + return this; } - getValue( array, offset ) { +} - this.bind(); // bind all binding +const loading = {}; - const firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; +class FileLoader extends Loader { - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); + constructor( manager ) { + + super( manager ); } - setValue( array, offset ) { + load( url, onLoad, onProgress, onError ) { - const bindings = this._bindings; + if ( url === undefined ) url = ''; - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + if ( this.path !== undefined ) url = this.path + url; - bindings[ i ].setValue( array, offset ); + url = this.manager.resolveURL( url ); - } + const cached = Cache.get( url ); - } + if ( cached !== undefined ) { - bind() { + this.manager.itemStart( url ); - const bindings = this._bindings; + setTimeout( () => { - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + if ( onLoad ) onLoad( cached ); - bindings[ i ].bind(); + this.manager.itemEnd( url ); - } + }, 0 ); - } + return cached; - unbind() { + } - const bindings = this._bindings; + // Check if request is duplicate - for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + if ( loading[ url ] !== undefined ) { - bindings[ i ].unbind(); + loading[ url ].push( { - } + onLoad: onLoad, + onProgress: onProgress, + onError: onError - } + } ); -} + return; -// Note: This class uses a State pattern on a per-method basis: -// 'bind' sets 'this.getValue' / 'setValue' and shadows the -// prototype version of these methods with one that represents -// the bound state. When the property is not found, the methods -// become no-ops. -class PropertyBinding { + } - constructor( rootNode, path, parsedPath ) { + // Initialise array for duplicate requests + loading[ url ] = []; - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); + loading[ url ].push( { + onLoad: onLoad, + onProgress: onProgress, + onError: onError, + } ); - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; + // create request + const req = new Request( url, { + headers: new Headers( this.requestHeader ), + credentials: this.withCredentials ? 'include' : 'same-origin', + // An abort controller could be added within a future PR + } ); - this.rootNode = rootNode; + // record states ( avoid data race ) + const mimeType = this.mimeType; + const responseType = this.responseType; - // initial state of these methods that calls 'bind' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; + // start the fetch + fetch( req ) + .then( response => { - } + if ( response.status === 200 || response.status === 0 ) { + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - static create( root, path, parsedPath ) { + if ( response.status === 0 ) { - if ( ! ( root && root.isAnimationObjectGroup ) ) { + console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - return new PropertyBinding( root, path, parsedPath ); + } - } else { + // Workaround: Checking if response.body === undefined for Alipay browser #23548 - return new PropertyBinding.Composite( root, path, parsedPath ); + if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) { - } + return response; - } + } - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - static sanitizeNodeName( name ) { + const callbacks = loading[ url ]; + const reader = response.body.getReader(); + const contentLength = response.headers.get( 'Content-Length' ); + const total = contentLength ? parseInt( contentLength ) : 0; + const lengthComputable = total !== 0; + let loaded = 0; - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); + // periodically read data into the new stream tracking while download progress + const stream = new ReadableStream( { + start( controller ) { - } + readData(); - static parseTrackName( trackName ) { + function readData() { - const matches = _trackRe.exec( trackName ); + reader.read().then( ( { done, value } ) => { - if ( ! matches ) { + if ( done ) { - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); + controller.close(); - } + } else { - const results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; + loaded += value.byteLength; - const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); + const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } ); + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - if ( lastDot !== undefined && lastDot !== - 1 ) { + const callback = callbacks[ i ]; + if ( callback.onProgress ) callback.onProgress( event ); - const objectName = results.nodeName.substring( lastDot + 1 ); + } - // Object names must be checked against an allowlist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { + controller.enqueue( value ); + readData(); - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; + } - } + } ); - } + } - if ( results.propertyName === null || results.propertyName.length === 0 ) { + } - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); + } ); - } + return new Response( stream ); - return results; + } else { - } + throw Error( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}` ); - static findNode( root, nodeName ) { + } - if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { + } ) + .then( response => { - return root; + switch ( responseType ) { - } + case 'arraybuffer': - // search into skeleton bones. - if ( root.skeleton ) { + return response.arrayBuffer(); - const bone = root.skeleton.getBoneByName( nodeName ); + case 'blob': - if ( bone !== undefined ) { + return response.blob(); - return bone; + case 'document': - } + return response.text() + .then( text => { - } + const parser = new DOMParser(); + return parser.parseFromString( text, mimeType ); - // search into node subtree. - if ( root.children ) { + } ); - const searchNodeSubtree = function ( children ) { + case 'json': - for ( let i = 0; i < children.length; i ++ ) { + return response.json(); - const childNode = children[ i ]; + default: - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { + if ( mimeType === undefined ) { - return childNode; + return response.text(); - } + } else { - const result = searchNodeSubtree( childNode.children ); + // sniff encoding + const re = /charset="?([^;"\s]*)"?/i; + const exec = re.exec( mimeType ); + const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; + const decoder = new TextDecoder( label ); + return response.arrayBuffer().then( ab => decoder.decode( ab ) ); - if ( result ) return result; + } } - return null; - - }; + } ) + .then( data => { - const subTreeNode = searchNodeSubtree( root.children ); + // Add to cache only on HTTP success, so that we do not cache + // error response bodies as proper responses to requests. + Cache.add( url, data ); - if ( subTreeNode ) { + const callbacks = loading[ url ]; + delete loading[ url ]; - return subTreeNode; + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - } + const callback = callbacks[ i ]; + if ( callback.onLoad ) callback.onLoad( data ); - } + } - return null; + } ) + .catch( err => { - } + // Abort errors and other errors are handled the same - // these are used to "bind" a nonexistent property - _getValue_unavailable() {} - _setValue_unavailable() {} + const callbacks = loading[ url ]; - // Getters + if ( callbacks === undefined ) { - _getValue_direct( buffer, offset ) { + // When onLoad was called and url was deleted in `loading` + this.manager.itemError( url ); + throw err; - buffer[ offset ] = this.node[ this.propertyName ]; + } - } + delete loading[ url ]; - _getValue_array( buffer, offset ) { + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - const source = this.resolvedProperty; + const callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( err ); - for ( let i = 0, n = source.length; i !== n; ++ i ) { + } - buffer[ offset ++ ] = source[ i ]; + this.manager.itemError( url ); - } + } ) + .finally( () => { - } + this.manager.itemEnd( url ); - _getValue_arrayElement( buffer, offset ) { + } ); - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; + this.manager.itemStart( url ); } - _getValue_toArray( buffer, offset ) { + setResponseType( value ) { - this.resolvedProperty.toArray( buffer, offset ); + this.responseType = value; + return this; } - // Direct - - _setValue_direct( buffer, offset ) { + setMimeType( value ) { - this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.mimeType = value; + return this; } - _setValue_direct_setNeedsUpdate( buffer, offset ) { - - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; +} - } +class ImageLoader extends Loader { - _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { + constructor( manager ) { - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + super( manager ); } - // EntireArray + load( url, onLoad, onProgress, onError ) { - _setValue_array( buffer, offset ) { + if ( this.path !== undefined ) url = this.path + url; - const dest = this.resolvedProperty; + url = this.manager.resolveURL( url ); - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + const scope = this; - dest[ i ] = buffer[ offset ++ ]; + const cached = Cache.get( url ); - } + if ( cached !== undefined ) { - } + scope.manager.itemStart( url ); - _setValue_array_setNeedsUpdate( buffer, offset ) { + setTimeout( function () { - const dest = this.resolvedProperty; + if ( onLoad ) onLoad( cached ); - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + scope.manager.itemEnd( url ); - dest[ i ] = buffer[ offset ++ ]; + }, 0 ); + + return cached; } - this.targetObject.needsUpdate = true; + const image = createElementNS( 'img' ); - } + function onImageLoad() { - _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { + removeEventListeners(); - const dest = this.resolvedProperty; + Cache.add( url, this ); - for ( let i = 0, n = dest.length; i !== n; ++ i ) { + if ( onLoad ) onLoad( this ); - dest[ i ] = buffer[ offset ++ ]; + scope.manager.itemEnd( url ); } - this.targetObject.matrixWorldNeedsUpdate = true; + function onImageError( event ) { - } + removeEventListeners(); - // ArrayElement + if ( onError ) onError( event ); - _setValue_arrayElement( buffer, offset ) { + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + } - } + function removeEventListeners() { - _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + } - } + image.addEventListener( 'load', onImageLoad, false ); + image.addEventListener( 'error', onImageError, false ); - _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { + if ( url.slice( 0, 5 ) !== 'data:' ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - } + } - // HasToFromArray + scope.manager.itemStart( url ); - _setValue_fromArray( buffer, offset ) { + image.src = url; - this.resolvedProperty.fromArray( buffer, offset ); + return image; } - _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; +} - } +class CubeTextureLoader extends Loader { - _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { + constructor( manager ) { - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; + super( manager ); } - _getValue_unbound( targetArray, offset ) { + load( urls, onLoad, onProgress, onError ) { - this.bind(); - this.getValue( targetArray, offset ); + const texture = new CubeTexture(); - } + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - _setValue_unbound( sourceArray, offset ) { + let loaded = 0; - this.bind(); - this.setValue( sourceArray, offset ); + function loadTexture( i ) { - } + loader.load( urls[ i ], function ( image ) { - // create getter / setter pair for a property in the scene graph - bind() { + texture.images[ i ] = image; - let targetObject = this.node; - const parsedPath = this.parsedPath; + loaded ++; - const objectName = parsedPath.objectName; - const propertyName = parsedPath.propertyName; - let propertyIndex = parsedPath.propertyIndex; + if ( loaded === 6 ) { - if ( ! targetObject ) { + texture.needsUpdate = true; - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; + if ( onLoad ) onLoad( texture ); - this.node = targetObject; + } - } + }, undefined, onError ); - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; + } - // ensure there is a value node - if ( ! targetObject ) { + for ( let i = 0; i < urls.length; ++ i ) { - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; + loadTexture( i ); } - if ( objectName ) { + return texture; - let objectIndex = parsedPath.objectIndex; + } - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { +} - case 'materials': +class TextureLoader extends Loader { - if ( ! targetObject.material ) { + constructor( manager ) { - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; + super( manager ); - } + } - if ( ! targetObject.material.materials ) { + load( url, onLoad, onProgress, onError ) { - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; + const texture = new Texture(); - } + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - targetObject = targetObject.material.materials; + loader.load( url, function ( image ) { - break; + texture.image = image; + texture.needsUpdate = true; - case 'bones': + if ( onLoad !== undefined ) { - if ( ! targetObject.skeleton ) { + onLoad( texture ); - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; + } - } + }, onProgress, onError ); - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. + return texture; - targetObject = targetObject.skeleton.bones; + } - // support resolving morphTarget names into indices. - for ( let i = 0; i < targetObject.length; i ++ ) { +} - if ( targetObject[ i ].name === objectIndex ) { +class Light extends Object3D { - objectIndex = i; - break; + constructor( color, intensity = 1 ) { - } + super(); - } + this.type = 'Light'; - break; + this.color = new Color( color ); + this.intensity = intensity; - default: + } - if ( targetObject[ objectName ] === undefined ) { + dispose() { - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; + // Empty here in base class; some subclasses override. - } + } - targetObject = targetObject[ objectName ]; + copy( source ) { - } + super.copy( source ); + this.color.copy( source.color ); + this.intensity = source.intensity; - if ( objectIndex !== undefined ) { + return this; - if ( targetObject[ objectIndex ] === undefined ) { + } - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; + toJSON( meta ) { - } + const data = super.toJSON( meta ); - targetObject = targetObject[ objectIndex ]; + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - } + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - } + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - // resolve property - const nodeProperty = targetObject[ propertyName ]; + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - if ( nodeProperty === undefined ) { + return data; - const nodeName = parsedPath.nodeName; + } - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; +} - } +Light.prototype.isLight = true; - // determine versioning scheme - let versioning = this.Versioning.None; +class HemisphereLight extends Light { - this.targetObject = targetObject; + constructor( skyColor, groundColor, intensity ) { - if ( targetObject.needsUpdate !== undefined ) { // material + super( skyColor, intensity ); - versioning = this.Versioning.NeedsUpdate; + this.type = 'HemisphereLight'; - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - versioning = this.Versioning.MatrixWorldNeedsUpdate; + this.groundColor = new Color( groundColor ); - } + } - // determine how the property gets bound - let bindingType = this.BindingType.Direct; + copy( source ) { - if ( propertyIndex !== undefined ) { + Light.prototype.copy.call( this, source ); - // access a sub element of the property array (only primitives are supported right now) + this.groundColor.copy( source.groundColor ); - if ( propertyName === 'morphTargetInfluences' ) { + return this; - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + } - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { +} - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; +HemisphereLight.prototype.isHemisphereLight = true; - } +const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); +const _lookTarget$1 = /*@__PURE__*/ new Vector3(); - if ( targetObject.geometry.isBufferGeometry ) { +class LightShadow { - if ( ! targetObject.geometry.morphAttributes ) { + constructor( camera ) { - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; + this.camera = camera; - } + this.bias = 0; + this.normalBias = 0; + this.radius = 1; + this.blurSamples = 8; - if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { + this.mapSize = new Vector2( 512, 512 ); - propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; + this.map = null; + this.mapPass = null; + this.matrix = new Matrix4(); - } + this.autoUpdate = true; + this.needsUpdate = false; + this._frustum = new Frustum(); + this._frameExtents = new Vector2( 1, 1 ); - } else { + this._viewportCount = 1; - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); - return; + this._viewports = [ - } + new Vector4( 0, 0, 1, 1 ) - } + ]; - bindingType = this.BindingType.ArrayElement; + } - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; + getViewportCount() { - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { + return this._viewportCount; - // must use copy for Object3D.Euler/Quaternion + } - bindingType = this.BindingType.HasFromToArray; + getFrustum() { - this.resolvedProperty = nodeProperty; + return this._frustum; - } else if ( Array.isArray( nodeProperty ) ) { + } - bindingType = this.BindingType.EntireArray; + updateMatrices( light ) { - this.resolvedProperty = nodeProperty; + const shadowCamera = this.camera; + const shadowMatrix = this.matrix; - } else { + _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld$1 ); - this.propertyName = propertyName; + _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget$1 ); + shadowCamera.updateMatrixWorld(); - } + _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); - } + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - unbind() { + } - this.node = null; + getViewport( viewportIndex ) { - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; + return this._viewports[ viewportIndex ]; } -} - -PropertyBinding.Composite = Composite; + getFrameExtents() { -PropertyBinding.prototype.BindingType = { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 -}; + return this._frameExtents; -PropertyBinding.prototype.Versioning = { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 -}; + } -PropertyBinding.prototype.GetterByBindingType = [ + dispose() { - PropertyBinding.prototype._getValue_direct, - PropertyBinding.prototype._getValue_array, - PropertyBinding.prototype._getValue_arrayElement, - PropertyBinding.prototype._getValue_toArray, + if ( this.map ) { -]; + this.map.dispose(); -PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ + } - [ - // Direct - PropertyBinding.prototype._setValue_direct, - PropertyBinding.prototype._setValue_direct_setNeedsUpdate, - PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, + if ( this.mapPass ) { - ], [ + this.mapPass.dispose(); - // EntireArray + } - PropertyBinding.prototype._setValue_array, - PropertyBinding.prototype._setValue_array_setNeedsUpdate, - PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, + } - ], [ + copy( source ) { - // ArrayElement - PropertyBinding.prototype._setValue_arrayElement, - PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, - PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, + this.camera = source.camera.clone(); - ], [ + this.bias = source.bias; + this.radius = source.radius; - // HasToFromArray - PropertyBinding.prototype._setValue_fromArray, - PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, - PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, + this.mapSize.copy( source.mapSize ); - ] + return this; -]; + } -class AnimationAction { + clone() { - constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { + return new this.constructor().copy( this ); - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot; - this.blendMode = blendMode; + } - const tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); + toJSON() { - const interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; + const object = {}; - for ( let i = 0; i !== nTracks; ++ i ) { + if ( this.bias !== 0 ) object.bias = this.bias; + if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; + if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - const interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; + object.camera = this.camera.toJSON( false ).object; + delete object.camera.matrix; - } + return object; - this._interpolantSettings = interpolantSettings; + } - this._interpolants = interpolants; // bound by the mixer +} - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); +class SpotLightShadow extends LightShadow { - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager + constructor() { - this._timeScaleInterpolant = null; - this._weightInterpolant = null; + super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - this.loop = LoopRepeat; - this._loopCount = - 1; + this.focus = 1; - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; + } - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; + updateMatrices( light ) { - this.timeScale = 1; - this._effectiveTimeScale = 1; + const camera = this.camera; - this.weight = 1; - this._effectiveWeight = 1; + const fov = RAD2DEG * 2 * light.angle * this.focus; + const aspect = this.mapSize.width / this.mapSize.height; + const far = light.distance || camera.far; - this.repetitions = Infinity; // no. of repetitions when looping + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); - this.clampWhenFinished = false;// keep feeding the last frame? + } - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end + super.updateMatrices( light ); } - // State & Scheduling + copy( source ) { - play() { + super.copy( source ); - this._mixer._activateAction( this ); + this.focus = source.focus; return this; } - stop() { - - this._mixer._deactivateAction( this ); +} - return this.reset(); +SpotLightShadow.prototype.isSpotLightShadow = true; - } +class SpotLight extends Light { - reset() { + constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 1 ) { - this.paused = false; - this.enabled = true; + super( color, intensity ); - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling + this.type = 'SpotLight'; - return this.stopFading().stopWarping(); + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - } + this.target = new Object3D(); - isRunning() { + this.distance = distance; + this.angle = angle; + this.penumbra = penumbra; + this.decay = decay; // for physically correct lights, should be 2. - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); + this.shadow = new SpotLightShadow(); } - // return true when play has been called - isScheduled() { + get power() { - return this._mixer._isActiveAction( this ); + // compute the light's luminous power (in lumens) from its intensity (in candela) + // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) + return this.intensity * Math.PI; } - startAt( time ) { - - this._startTime = time; + set power( power ) { - return this; + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / Math.PI; } - setLoop( mode, repetitions ) { - - this.loop = mode; - this.repetitions = repetitions; + dispose() { - return this; + this.shadow.dispose(); } - // Weight - - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight( weight ) { - - this.weight = weight; + copy( source ) { - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; + super.copy( source ); - return this.stopFading(); + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - } + this.target = source.target.clone(); - // return the weight considering fading and .enabled - getEffectiveWeight() { + this.shadow = source.shadow.clone(); - return this._effectiveWeight; + return this; } - fadeIn( duration ) { - - return this._scheduleFading( duration, 0, 1 ); - - } +} - fadeOut( duration ) { +SpotLight.prototype.isSpotLight = true; - return this._scheduleFading( duration, 1, 0 ); +const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld = /*@__PURE__*/ new Vector3(); +const _lookTarget = /*@__PURE__*/ new Vector3(); - } +class PointLightShadow extends LightShadow { - crossFadeFrom( fadeOutAction, duration, warp ) { + constructor() { - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); + super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - if ( warp ) { + this._frameExtents = new Vector2( 4, 2 ); - const fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, + this._viewportCount = 6; - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; + this._viewports = [ + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); + // positive X + new Vector4( 2, 1, 1, 1 ), + // negative X + new Vector4( 0, 1, 1, 1 ), + // positive Z + new Vector4( 3, 1, 1, 1 ), + // negative Z + new Vector4( 1, 1, 1, 1 ), + // positive Y + new Vector4( 3, 0, 1, 1 ), + // negative Y + new Vector4( 1, 0, 1, 1 ) + ]; - } + this._cubeDirections = [ + new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), + new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) + ]; - return this; + this._cubeUps = [ + new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), + new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) + ]; } - crossFadeTo( fadeInAction, duration, warp ) { - - return fadeInAction.crossFadeFrom( this, duration, warp ); - - } + updateMatrices( light, viewportIndex = 0 ) { - stopFading() { + const camera = this.camera; + const shadowMatrix = this.matrix; - const weightInterpolant = this._weightInterpolant; + const far = light.distance || camera.far; - if ( weightInterpolant !== null ) { + if ( far !== camera.far ) { - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); + camera.far = far; + camera.updateProjectionMatrix(); } - return this; + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + camera.position.copy( _lightPositionWorld ); - } + _lookTarget.copy( camera.position ); + _lookTarget.add( this._cubeDirections[ viewportIndex ] ); + camera.up.copy( this._cubeUps[ viewportIndex ] ); + camera.lookAt( _lookTarget ); + camera.updateMatrixWorld(); - // Time Scale Control + shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale( timeScale ) { + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix ); - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; + } - return this.stopWarping(); +} - } +PointLightShadow.prototype.isPointLightShadow = true; - // return the time scale considering warping and .paused - getEffectiveTimeScale() { +class PointLight extends Light { - return this._effectiveTimeScale; + constructor( color, intensity, distance = 0, decay = 1 ) { - } + super( color, intensity ); - setDuration( duration ) { + this.type = 'PointLight'; - this.timeScale = this._clip.duration / duration; + this.distance = distance; + this.decay = decay; // for physically correct lights, should be 2. - return this.stopWarping(); + this.shadow = new PointLightShadow(); } - syncWith( action ) { - - this.time = action.time; - this.timeScale = action.timeScale; + get power() { - return this.stopWarping(); + // compute the light's luminous power (in lumens) from its intensity (in candela) + // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) + return this.intensity * 4 * Math.PI; } - halt( duration ) { + set power( power ) { - return this.warp( this._effectiveTimeScale, 0, duration ); + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / ( 4 * Math.PI ); } - warp( startTimeScale, endTimeScale, duration ) { - - const mixer = this._mixer, - now = mixer.time, - timeScale = this.timeScale; - - let interpolant = this._timeScaleInterpolant; + dispose() { - if ( interpolant === null ) { + this.shadow.dispose(); - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; + } - } + copy( source ) { - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; + super.copy( source ); - times[ 0 ] = now; - times[ 1 ] = now + duration; + this.distance = source.distance; + this.decay = source.decay; - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; + this.shadow = source.shadow.clone(); return this; } - stopWarping() { - - const timeScaleInterpolant = this._timeScaleInterpolant; +} - if ( timeScaleInterpolant !== null ) { +PointLight.prototype.isPointLight = true; - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); +class DirectionalLightShadow extends LightShadow { - } + constructor() { - return this; + super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } - // Object Accessors +} - getMixer() { +DirectionalLightShadow.prototype.isDirectionalLightShadow = true; - return this._mixer; +class DirectionalLight extends Light { - } + constructor( color, intensity ) { - getClip() { + super( color, intensity ); - return this._clip; + this.type = 'DirectionalLight'; - } + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - getRoot() { + this.target = new Object3D(); - return this._localRoot || this._mixer._root; + this.shadow = new DirectionalLightShadow(); } - // Interna - - _update( time, deltaTime, timeDirection, accuIndex ) { + dispose() { - // called by the mixer + this.shadow.dispose(); - if ( ! this.enabled ) { + } - // call ._updateWeight() to update ._effectiveWeight + copy( source ) { - this._updateWeight( time ); - return; + super.copy( source ); - } + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); - const startTime = this._startTime; + return this; - if ( startTime !== null ) { + } - // check for scheduled start of action +} - const timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { +DirectionalLight.prototype.isDirectionalLight = true; - return; // yet to come / don't decide when delta = 0 +class AmbientLight extends Light { - } + constructor( color, intensity ) { - // start + super( color, intensity ); - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; + this.type = 'AmbientLight'; - } + } - // apply time scale and advance time +} - deltaTime *= this._updateTimeScale( time ); - const clipTime = this._updateTime( deltaTime ); +AmbientLight.prototype.isAmbientLight = true; - // note: _updateTime may disable the action resulting in - // an effective weight of 0 +class RectAreaLight extends Light { - const weight = this._updateWeight( time ); + constructor( color, intensity, width = 10, height = 10 ) { - if ( weight > 0 ) { + super( color, intensity ); - const interpolants = this._interpolants; - const propertyMixers = this._propertyBindings; + this.type = 'RectAreaLight'; - switch ( this.blendMode ) { + this.width = width; + this.height = height; - case AdditiveAnimationBlendMode: + } - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { + get power() { - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulateAdditive( weight ); + // compute the light's luminous power (in lumens) from its intensity (in nits) + return this.intensity * this.width * this.height * Math.PI; - } + } - break; + set power( power ) { - case NormalAnimationBlendMode: - default: + // set the light's intensity (in nits) from the desired luminous power (in lumens) + this.intensity = power / ( this.width * this.height * Math.PI ); - for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { + } - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); + copy( source ) { - } + super.copy( source ); - } + this.width = source.width; + this.height = source.height; - } + return this; } - _updateWeight( time ) { + toJSON( meta ) { - let weight = 0; + const data = super.toJSON( meta ); - if ( this.enabled ) { + data.object.width = this.width; + data.object.height = this.height; - weight = this.weight; - const interpolant = this._weightInterpolant; + return data; - if ( interpolant !== null ) { + } - const interpolantValue = interpolant.evaluate( time )[ 0 ]; +} - weight *= interpolantValue; +RectAreaLight.prototype.isRectAreaLight = true; - if ( time > interpolant.parameterPositions[ 1 ] ) { +/** + * Primary reference: + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * Secondary reference: + * https://www.ppsloan.org/publications/StupidSH36.pdf + */ - this.stopFading(); +// 3-band SH defined by 9 coefficients - if ( interpolantValue === 0 ) { +class SphericalHarmonics3 { - // faded out, disable - this.enabled = false; + constructor() { - } + this.coefficients = []; - } + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients.push( new Vector3() ); } - this._effectiveWeight = weight; - return weight; - } - _updateTimeScale( time ) { - - let timeScale = 0; - - if ( ! this.paused ) { - - timeScale = this.timeScale; - - const interpolant = this._timeScaleInterpolant; - - if ( interpolant !== null ) { - - const interpolantValue = interpolant.evaluate( time )[ 0 ]; - - timeScale *= interpolantValue; - - if ( time > interpolant.parameterPositions[ 1 ] ) { + set( coefficients ) { - this.stopWarping(); + for ( let i = 0; i < 9; i ++ ) { - if ( timeScale === 0 ) { + this.coefficients[ i ].copy( coefficients[ i ] ); - // motion has halted, pause - this.paused = true; + } - } else { + return this; - // warp done - apply final time scale - this.timeScale = timeScale; + } - } + zero() { - } + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].set( 0, 0, 0 ); } - this._effectiveTimeScale = timeScale; - return timeScale; + return this; } - _updateTime( deltaTime ) { + // get the radiance in the direction of the normal + // target is a Vector3 + getAt( normal, target ) { - const duration = this._clip.duration; - const loop = this.loop; + // normal is assumed to be unit length - let time = this.time + deltaTime; - let loopCount = this._loopCount; + const x = normal.x, y = normal.y, z = normal.z; - const pingPong = ( loop === LoopPingPong ); + const coeff = this.coefficients; - if ( deltaTime === 0 ) { + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - if ( loopCount === - 1 ) return time; + // band 1 + target.addScaledVector( coeff[ 1 ], 0.488603 * y ); + target.addScaledVector( coeff[ 2 ], 0.488603 * z ); + target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; + // band 2 + target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); + target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); + target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); + target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); + target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); - } + return target; - if ( loop === LoopOnce ) { + } - if ( loopCount === - 1 ) { + // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal + // target is a Vector3 + // https://graphics.stanford.edu/papers/envmap/envmap.pdf + getIrradianceAt( normal, target ) { - // just started + // normal is assumed to be unit length - this._loopCount = 0; - this._setEndings( true, true, false ); + const x = normal.x, y = normal.y, z = normal.z; - } + const coeff = this.coefficients; - handle_stop: { + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - if ( time >= duration ) { + // band 1 + target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 + target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); + target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - time = duration; + // band 2 + target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 + target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); + target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 + target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); + target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - } else if ( time < 0 ) { + return target; - time = 0; + } - } else { + add( sh ) { - this.time = time; + for ( let i = 0; i < 9; i ++ ) { - break handle_stop; + this.coefficients[ i ].add( sh.coefficients[ i ] ); - } + } - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + return this; - this.time = time; + } - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); + addScaledSH( sh, s ) { - } + for ( let i = 0; i < 9; i ++ ) { - } else { // repetitive Repeat or PingPong + this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - if ( loopCount === - 1 ) { + } - // just started + return this; - if ( deltaTime >= 0 ) { + } - loopCount = 0; + scale( s ) { - this._setEndings( true, this.repetitions === 0, pingPong ); + for ( let i = 0; i < 9; i ++ ) { - } else { + this.coefficients[ i ].multiplyScalar( s ); - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 + } - this._setEndings( this.repetitions === 0, true, pingPong ); + return this; - } + } - } + lerp( sh, alpha ) { - if ( time >= duration || time < 0 ) { + for ( let i = 0; i < 9; i ++ ) { - // wrap around + this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - const loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; + } - loopCount += Math.abs( loopDelta ); + return this; - const pending = this.repetitions - loopCount; + } - if ( pending <= 0 ) { + equals( sh ) { - // have to stop (switch state, clamp time, fire event) + for ( let i = 0; i < 9; i ++ ) { - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - time = deltaTime > 0 ? duration : 0; + return false; - this.time = time; + } - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); + } - } else { + return true; - // keep running + } - if ( pending === 1 ) { + copy( sh ) { - // entering the last round + return this.set( sh.coefficients ); - const atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); + } - } else { + clone() { - this._setEndings( false, false, pingPong ); + return new this.constructor().copy( this ); - } + } - this._loopCount = loopCount; + fromArray( array, offset = 0 ) { - this.time = time; + const coefficients = this.coefficients; - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - } else { + } - this.time = time; + return this; - } + } - if ( pingPong && ( loopCount & 1 ) === 1 ) { + toArray( array = [], offset = 0 ) { - // invert time for the "pong round" + const coefficients = this.coefficients; - return duration - time; + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } - return time; + return array; } - _setEndings( atStart, atEnd, pingPong ) { + // evaluate the basis functions + // shBasis is an Array[ 9 ] + static getBasisAt( normal, shBasis ) { - const settings = this._interpolantSettings; + // normal is assumed to be unit length - if ( pingPong ) { + const x = normal.x, y = normal.y, z = normal.z; - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; + // band 0 + shBasis[ 0 ] = 0.282095; - } else { + // band 1 + shBasis[ 1 ] = 0.488603 * y; + shBasis[ 2 ] = 0.488603 * z; + shBasis[ 3 ] = 0.488603 * x; - // assuming for LoopOnce atStart == atEnd == true + // band 2 + shBasis[ 4 ] = 1.092548 * x * y; + shBasis[ 5 ] = 1.092548 * y * z; + shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); + shBasis[ 7 ] = 1.092548 * x * z; + shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); - if ( atStart ) { + } - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; +} - } else { +SphericalHarmonics3.prototype.isSphericalHarmonics3 = true; - settings.endingStart = WrapAroundEnding; +class LightProbe extends Light { - } + constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { - if ( atEnd ) { + super( undefined, intensity ); - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; + this.sh = sh; - } else { + } - settings.endingEnd = WrapAroundEnding; + copy( source ) { - } + super.copy( source ); - } + this.sh.copy( source.sh ); + + return this; } - _scheduleFading( duration, weightNow, weightThen ) { + fromJSON( json ) { - const mixer = this._mixer, now = mixer.time; - let interpolant = this._weightInterpolant; + this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); + this.sh.fromArray( json.sh ); - if ( interpolant === null ) { + return this; - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; + } - } + toJSON( meta ) { - const times = interpolant.parameterPositions, - values = interpolant.sampleValues; + const data = super.toJSON( meta ); - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; + data.object.sh = this.sh.toArray(); - return this; + return data; } } -class AnimationMixer extends EventDispatcher$1 { +LightProbe.prototype.isLightProbe = true; - constructor( root ) { +class LoaderUtils { - super(); + static decodeText( array ) { - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; - this.time = 0; - this.timeScale = 1.0; + if ( typeof TextDecoder !== 'undefined' ) { - } + return new TextDecoder().decode( array ); - _bindAction( action, prototypeAction ) { + } - const root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName; + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. - let bindingsByName = bindingsByRoot[ rootUuid ]; + let s = ''; - if ( bindingsByName === undefined ) { + for ( let i = 0, il = array.length; i < il; i ++ ) { - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; + // Implicitly assumes little-endian. + s += String.fromCharCode( array[ i ] ); } - for ( let i = 0; i !== nTracks; ++ i ) { + try { - const track = tracks[ i ], - trackName = track.name; + // merges multi-byte utf-8 characters. - let binding = bindingsByName[ trackName ]; + return decodeURIComponent( escape( s ) ); - if ( binding !== undefined ) { + } catch ( e ) { // see #16358 - bindings[ i ] = binding; + return s; - } else { + } - binding = bindings[ i ]; + } - if ( binding !== undefined ) { + static extractUrlBase( url ) { - // existing binding, make sure the cache knows + const index = url.lastIndexOf( '/' ); - if ( binding._cacheIndex === null ) { + if ( index === - 1 ) return './'; - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + return url.slice( 0, index + 1 ); - } + } - continue; + static resolveURL( url, path ) { - } + // Invalid URL + if ( typeof url !== 'string' || url === '' ) return ''; - const path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; + // Host Relative URL + if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); + path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + } - bindings[ i ] = binding; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) return url; - } + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) return url; - interpolants[ i ].resultBuffer = binding.buffer; + // Blob URL + if ( /^blob:.*$/i.test( url ) ) return url; - } + // Relative URL + return path + url; } - _activateAction( action ) { +} - if ( ! this._isActiveAction( action ) ) { +class InstancedBufferGeometry extends BufferGeometry { - if ( action._cacheIndex === null ) { + constructor() { - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind + super(); - const rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; + this.type = 'InstancedBufferGeometry'; + this.instanceCount = Infinity; - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); + } - this._addInactiveAction( action, clipUuid, rootUuid ); + copy( source ) { - } + super.copy( source ); - const bindings = action._propertyBindings; + this.instanceCount = source.instanceCount; - // increment reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + return this; - const binding = bindings[ i ]; + } - if ( binding.useCount ++ === 0 ) { + clone() { - this._lendBinding( binding ); - binding.saveOriginalState(); + return new this.constructor().copy( this ); - } + } - } + toJSON() { - this._lendAction( action ); + const data = super.toJSON( this ); - } + data.instanceCount = this.instanceCount; + + data.isInstancedBufferGeometry = true; + + return data; } - _deactivateAction( action ) { +} - if ( this._isActiveAction( action ) ) { +InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; - const bindings = action._propertyBindings; +class ImageBitmapLoader extends Loader { - // decrement reference counts / sort out state - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + constructor( manager ) { - const binding = bindings[ i ]; + super( manager ); - if ( -- binding.useCount === 0 ) { + if ( typeof createImageBitmap === 'undefined' ) { - binding.restoreOriginalState(); - this._takeBackBinding( binding ); + console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - } + } - } + if ( typeof fetch === 'undefined' ) { - this._takeBackAction( action ); + console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); } - } + this.options = { premultiplyAlpha: 'none' }; - // Memory manager + } - _initMemoryManager() { + setOptions( options ) { - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; + this.options = options; - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } + return this; + } - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; + load( url, onLoad, onProgress, onError ) { - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > + if ( url === undefined ) url = ''; + if ( this.path !== undefined ) url = this.path + url; - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; + url = this.manager.resolveURL( url ); const scope = this; - this.stats = { - - actions: { - get total() { + const cached = Cache.get( url ); - return scope._actions.length; + if ( cached !== undefined ) { - }, - get inUse() { + scope.manager.itemStart( url ); - return scope._nActiveActions; + setTimeout( function () { - } - }, - bindings: { - get total() { + if ( onLoad ) onLoad( cached ); - return scope._bindings.length; + scope.manager.itemEnd( url ); - }, - get inUse() { + }, 0 ); - return scope._nActiveBindings; + return cached; - } - }, - controlInterpolants: { - get total() { + } - return scope._controlInterpolants.length; + const fetchOptions = {}; + fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; + fetchOptions.headers = this.requestHeader; - }, - get inUse() { + fetch( url, fetchOptions ).then( function ( res ) { - return scope._nActiveControlInterpolants; + return res.blob(); - } - } + } ).then( function ( blob ) { - }; + return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); - } + } ).then( function ( imageBitmap ) { - // Memory management for AnimationAction objects + Cache.add( url, imageBitmap ); - _isActiveAction( action ) { + if ( onLoad ) onLoad( imageBitmap ); - const index = action._cacheIndex; - return index !== null && index < this._nActiveActions; + scope.manager.itemEnd( url ); - } + } ).catch( function ( e ) { - _addInactiveAction( action, clipUuid, rootUuid ) { + if ( onError ) onError( e ); - const actions = this._actions, - actionsByClip = this._actionsByClip; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - let actionsForClip = actionsByClip[ clipUuid ]; + } ); - if ( actionsForClip === undefined ) { + scope.manager.itemStart( url ); - actionsForClip = { + } - knownActions: [ action ], - actionByRoot: {} +} - }; +ImageBitmapLoader.prototype.isImageBitmapLoader = true; - action._byClipCacheIndex = 0; +let _context; - actionsByClip[ clipUuid ] = actionsForClip; +const AudioContext = { - } else { + getContext: function () { - const knownActions = actionsForClip.knownActions; + if ( _context === undefined ) { - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); + _context = new ( window.AudioContext || window.webkitAudioContext )(); } - action._cacheIndex = actions.length; - actions.push( action ); - - actionsForClip.actionByRoot[ rootUuid ] = action; - - } + return _context; - _removeInactiveAction( action ) { + }, - const actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; + setContext: function ( value ) { - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); + _context = value; - action._cacheIndex = null; + } +}; - const clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, +class AudioLoader extends Loader { - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], + constructor( manager ) { - byClipCacheIndex = action._byClipCacheIndex; + super( manager ); - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); + } - action._byClipCacheIndex = null; + load( url, onLoad, onProgress, onError ) { + const scope = this; - const actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; + const loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( buffer ) { - delete actionByRoot[ rootUuid ]; + try { - if ( knownActionsForClip.length === 0 ) { + // Create a copy of the buffer. The `decodeAudioData` method + // detaches the buffer when complete, preventing reuse. + const bufferCopy = buffer.slice( 0 ); - delete actionsByClip[ clipUuid ]; + const context = AudioContext.getContext(); + context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - } + onLoad( audioBuffer ); - this._removeInactiveBindingsForAction( action ); + } ); - } + } catch ( e ) { - _removeInactiveBindingsForAction( action ) { + if ( onError ) { - const bindings = action._propertyBindings; + onError( e ); - for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + } else { - const binding = bindings[ i ]; + console.error( e ); - if ( -- binding.referenceCount === 0 ) { + } - this._removeInactiveBinding( binding ); + scope.manager.itemError( url ); } - } + }, onProgress, onError ); } - _lendAction( action ) { +} - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s +class HemisphereLightProbe extends LightProbe { - const actions = this._actions, - prevIndex = action._cacheIndex, + constructor( skyColor, groundColor, intensity = 1 ) { - lastActiveIndex = this._nActiveActions ++, + super( undefined, intensity ); - firstInactiveAction = actions[ lastActiveIndex ]; + const color1 = new Color().set( skyColor ); + const color2 = new Color().set( groundColor ); - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; + const sky = new Vector3( color1.r, color1.g, color1.b ); + const ground = new Vector3( color2.r, color2.g, color2.b ); - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; + // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); + const c0 = Math.sqrt( Math.PI ); + const c1 = c0 * Math.sqrt( 0.75 ); + + this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); + this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); } - _takeBackAction( action ) { +} - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a +HemisphereLightProbe.prototype.isHemisphereLightProbe = true; - const actions = this._actions, - prevIndex = action._cacheIndex, +class AmbientLightProbe extends LightProbe { - firstInactiveIndex = -- this._nActiveActions, + constructor( color, intensity = 1 ) { - lastActiveAction = actions[ firstInactiveIndex ]; + super( undefined, intensity ); - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; + const color1 = new Color().set( color ); - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; + // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); + this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); } - // Memory management for PropertyMixer objects - - _addInactiveBinding( binding, rootUuid, trackName ) { - - const bindingsByRoot = this._bindingsByRootAndName, - bindings = this._bindings; +} - let bindingByName = bindingsByRoot[ rootUuid ]; +AmbientLightProbe.prototype.isAmbientLightProbe = true; - if ( bindingByName === undefined ) { +class Clock { - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; + constructor( autoStart = true ) { - } + this.autoStart = autoStart; - bindingByName[ trackName ] = binding; + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; - binding._cacheIndex = bindings.length; - bindings.push( binding ); + this.running = false; } - _removeInactiveBinding( binding ) { - - const bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], - - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; + start() { - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); + this.startTime = now(); - delete bindingByName[ trackName ]; + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; - if ( Object.keys( bindingByName ).length === 0 ) { + } - delete bindingsByRoot[ rootUuid ]; + stop() { - } + this.getElapsedTime(); + this.running = false; + this.autoStart = false; } - _lendBinding( binding ) { + getElapsedTime() { - const bindings = this._bindings, - prevIndex = binding._cacheIndex, + this.getDelta(); + return this.elapsedTime; - lastActiveIndex = this._nActiveBindings ++, + } - firstInactiveBinding = bindings[ lastActiveIndex ]; + getDelta() { - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; + let diff = 0; - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; + if ( this.autoStart && ! this.running ) { - } + this.start(); + return 0; - _takeBackBinding( binding ) { + } - const bindings = this._bindings, - prevIndex = binding._cacheIndex, + if ( this.running ) { - firstInactiveIndex = -- this._nActiveBindings, + const newTime = now(); - lastActiveBinding = bindings[ firstInactiveIndex ]; + diff = ( newTime - this.oldTime ) / 1000; + this.oldTime = newTime; - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; + this.elapsedTime += diff; - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; + } + + return diff; } +} - // Memory management of Interpolants for weight and time scale +function now() { - _lendControlInterpolant() { + return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - const interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++; +} - let interpolant = interpolants[ lastActiveIndex ]; +class Audio extends Object3D { - if ( interpolant === undefined ) { + constructor( listener ) { - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); + super(); - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; + this.type = 'Audio'; - } + this.listener = listener; + this.context = listener.context; - return interpolant; + this.gain = this.context.createGain(); + this.gain.connect( listener.getInput() ); - } + this.autoplay = false; - _takeBackControlInterpolant( interpolant ) { + this.buffer = null; + this.detune = 0; + this.loop = false; + this.loopStart = 0; + this.loopEnd = 0; + this.offset = 0; + this.duration = undefined; + this.playbackRate = 1; + this.isPlaying = false; + this.hasPlaybackControl = true; + this.source = null; + this.sourceType = 'empty'; - const interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, + this._startedAt = 0; + this._progress = 0; + this._connected = false; - firstInactiveIndex = -- this._nActiveControlInterpolants, + this.filters = []; - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; + } - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; + getOutput() { - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; + return this.gain; } - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction( clip, optionalRoot, blendMode ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid; - - let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - - const clipUuid = clipObject !== null ? clipObject.uuid : clip; - - const actionsForClip = this._actionsByClip[ clipUuid ]; - let prototypeAction = null; - - if ( blendMode === undefined ) { - - if ( clipObject !== null ) { - - blendMode = clipObject.blendMode; - - } else { - - blendMode = NormalAnimationBlendMode; + setNodeSource( audioNode ) { - } + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); - } + return this; - if ( actionsForClip !== undefined ) { + } - const existingAction = actionsForClip.actionByRoot[ rootUuid ]; + setMediaElementSource( mediaElement ) { - if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { + this.hasPlaybackControl = false; + this.sourceType = 'mediaNode'; + this.source = this.context.createMediaElementSource( mediaElement ); + this.connect(); - return existingAction; + return this; - } + } - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; + setMediaStreamSource( mediaStream ) { - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; + this.hasPlaybackControl = false; + this.sourceType = 'mediaStreamNode'; + this.source = this.context.createMediaStreamSource( mediaStream ); + this.connect(); - } + return this; - // clip must be known when specified via string - if ( clipObject === null ) return null; + } - // allocate all resources required to run it - const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); + setBuffer( audioBuffer ) { - this._bindAction( newAction, prototypeAction ); + this.buffer = audioBuffer; + this.sourceType = 'buffer'; - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); + if ( this.autoplay ) this.play(); - return newAction; + return this; } - // get an existing action - existingAction( clip, optionalRoot ) { - - const root = optionalRoot || this._root, - rootUuid = root.uuid, + play( delay = 0 ) { - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, + if ( this.isPlaying === true ) { - clipUuid = clipObject ? clipObject.uuid : clip, + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; - actionsForClip = this._actionsByClip[ clipUuid ]; + } - if ( actionsForClip !== undefined ) { + if ( this.hasPlaybackControl === false ) { - return actionsForClip.actionByRoot[ rootUuid ] || null; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - return null; - - } - - // deactivates all previously scheduled actions - stopAllAction() { + this._startedAt = this.context.currentTime + delay; - const actions = this._actions, - nActions = this._nActiveActions; + const source = this.context.createBufferSource(); + source.buffer = this.buffer; + source.loop = this.loop; + source.loopStart = this.loopStart; + source.loopEnd = this.loopEnd; + source.onended = this.onEnded.bind( this ); + source.start( this._startedAt, this._progress + this.offset, this.duration ); - for ( let i = nActions - 1; i >= 0; -- i ) { + this.isPlaying = true; - actions[ i ].stop(); + this.source = source; - } + this.setDetune( this.detune ); + this.setPlaybackRate( this.playbackRate ); - return this; + return this.connect(); } - // advance the time and update apply the animation - update( deltaTime ) { - - deltaTime *= this.timeScale; + pause() { - const actions = this._actions, - nActions = this._nActiveActions, + if ( this.hasPlaybackControl === false ) { - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - accuIndex = this._accuIndex ^= 1; + } - // run active actions + if ( this.isPlaying === true ) { - for ( let i = 0; i !== nActions; ++ i ) { + // update current progress - const action = actions[ i ]; + this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - action._update( time, deltaTime, timeDirection, accuIndex ); + if ( this.loop === true ) { - } + // ensure _progress does not exceed duration with looped audios - // update scene graph + this._progress = this._progress % ( this.duration || this.buffer.duration ); - const bindings = this._bindings, - nBindings = this._nActiveBindings; + } - for ( let i = 0; i !== nBindings; ++ i ) { + this.source.stop(); + this.source.onended = null; - bindings[ i ].apply( accuIndex ); + this.isPlaying = false; } @@ -40165,6246 +40056,5887 @@ class AnimationMixer extends EventDispatcher$1 { } - // Allows you to seek to a specific time in an animation. - setTime( timeInSeconds ) { + stop() { - this.time = 0; // Zero out time attribute for AnimationMixer object; - for ( let i = 0; i < this._actions.length; i ++ ) { + if ( this.hasPlaybackControl === false ) { - this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - - } + this._progress = 0; - // return this mixer's root target object - getRoot() { + this.source.stop(); + this.source.onended = null; + this.isPlaying = false; - return this._root; + return this; } - // free all resources specific to a particular clip - uncacheClip( clip ) { - - const actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; - - if ( actionsForClip !== undefined ) { - - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away + connect() { - const actionsToRemove = actionsForClip.knownActions; + if ( this.filters.length > 0 ) { - for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { + this.source.connect( this.filters[ 0 ] ); - const action = actionsToRemove[ i ]; + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - this._deactivateAction( action ); + this.filters[ i - 1 ].connect( this.filters[ i ] ); - const cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; + } - action._cacheIndex = null; - action._byClipCacheIndex = null; + this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); + } else { - this._removeInactiveBindingsForAction( action ); + this.source.connect( this.getOutput() ); - } + } - delete actionsByClip[ clipUuid ]; + this._connected = true; - } + return this; } - // free all resources specific to a particular root target object - uncacheRoot( root ) { - - const rootUuid = root.uuid, - actionsByClip = this._actionsByClip; + disconnect() { - for ( const clipUuid in actionsByClip ) { + if ( this.filters.length > 0 ) { - const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; + this.source.disconnect( this.filters[ 0 ] ); - if ( action !== undefined ) { + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - this._deactivateAction( action ); - this._removeInactiveAction( action ); + this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } - } - - const bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; - - if ( bindingByName !== undefined ) { - - for ( const trackName in bindingByName ) { + this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - const binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); + } else { - } + this.source.disconnect( this.getOutput() ); } - } - - // remove a targeted clip from the cache - uncacheAction( clip, optionalRoot ) { - - const action = this.existingAction( clip, optionalRoot ); - - if ( action !== null ) { - - this._deactivateAction( action ); - this._removeInactiveAction( action ); + this._connected = false; - } + return this; } -} + getFilters() { -AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); + return this.filters; -class InstancedInterleavedBuffer extends InterleavedBuffer { + } - constructor( array, stride, meshPerAttribute = 1 ) { + setFilters( value ) { - super( array, stride ); + if ( ! value ) value = []; - this.meshPerAttribute = meshPerAttribute || 1; + if ( this._connected === true ) { - } + this.disconnect(); + this.filters = value.slice(); + this.connect(); - copy( source ) { + } else { - super.copy( source ); + this.filters = value.slice(); - this.meshPerAttribute = source.meshPerAttribute; + } return this; } - clone( data ) { - - const ib = super.clone( data ); - - ib.meshPerAttribute = this.meshPerAttribute; + setDetune( value ) { - return ib; + this.detune = value; - } + if ( this.source.detune === undefined ) return; // only set detune when available - toJSON( data ) { + if ( this.isPlaying === true ) { - const json = super.toJSON( data ); + this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - json.isInstancedInterleavedBuffer = true; - json.meshPerAttribute = this.meshPerAttribute; + } - return json; + return this; } -} - -InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; - -class Raycaster { - - constructor( origin, direction, near = 0, far = Infinity ) { - - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) - - this.near = near; - this.far = far; - this.camera = null; - this.layers = new Layers(); + getDetune() { - this.params = { - Mesh: {}, - Line: { threshold: 1 }, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; + return this.detune; } - set( origin, direction ) { - - // direction is assumed to be normalized (for accurate distance calculations) + getFilter() { - this.ray.set( origin, direction ); + return this.getFilters()[ 0 ]; } - setFromCamera( coords, camera ) { - - if ( camera && camera.isPerspectiveCamera ) { + setFilter( filter ) { - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; + return this.setFilters( filter ? [ filter ] : [] ); - } else if ( camera && camera.isOrthographicCamera ) { + } - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; + setPlaybackRate( value ) { - } else { + if ( this.hasPlaybackControl === false ) { - console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - } + this.playbackRate = value; - intersectObject( object, recursive = false, intersects = [] ) { + if ( this.isPlaying === true ) { - intersectObject( object, this, intersects, recursive ); + this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - intersects.sort( ascSort ); + } - return intersects; + return this; } - intersectObjects( objects, recursive = false, intersects = [] ) { - - for ( let i = 0, l = objects.length; i < l; i ++ ) { + getPlaybackRate() { - intersectObject( objects[ i ], this, intersects, recursive ); + return this.playbackRate; - } + } - intersects.sort( ascSort ); + onEnded() { - return intersects; + this.isPlaying = false; } -} - -function ascSort( a, b ) { - - return a.distance - b.distance; + getLoop() { -} + if ( this.hasPlaybackControl === false ) { -function intersectObject( object, raycaster, intersects, recursive ) { + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return false; - if ( object.layers.test( raycaster.layers ) ) { + } - object.raycast( raycaster, intersects ); + return this.loop; } - if ( recursive === true ) { - - const children = object.children; + setLoop( value ) { - for ( let i = 0, l = children.length; i < l; i ++ ) { + if ( this.hasPlaybackControl === false ) { - intersectObject( children[ i ], raycaster, intersects, true ); + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; } - } - -} - -/** - * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system - * - * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. - * The azimuthal angle (theta) is measured from the positive z-axis. - */ + this.loop = value; -class Spherical { + if ( this.isPlaying === true ) { - constructor( radius = 1, phi = 0, theta = 0 ) { + this.source.loop = this.loop; - this.radius = radius; - this.phi = phi; // polar angle - this.theta = theta; // azimuthal angle + } return this; } - set( radius, phi, theta ) { + setLoopStart( value ) { - this.radius = radius; - this.phi = phi; - this.theta = theta; + this.loopStart = value; return this; } - copy( other ) { + setLoopEnd( value ) { - this.radius = other.radius; - this.phi = other.phi; - this.theta = other.theta; + this.loopEnd = value; return this; } - // restrict phi to be betwee EPS and PI-EPS - makeSafe() { - - const EPS = 0.000001; - this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); + getVolume() { - return this; + return this.gain.gain.value; } - setFromVector3( v ) { + setVolume( value ) { - return this.setFromCartesianCoords( v.x, v.y, v.z ); + this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - } + return this; - setFromCartesianCoords( x, y, z ) { + } - this.radius = Math.sqrt( x * x + y * y + z * z ); +} - if ( this.radius === 0 ) { +class PropertyMixer { - this.theta = 0; - this.phi = 0; + constructor( binding, typeName, valueSize ) { - } else { + this.binding = binding; + this.valueSize = valueSize; - this.theta = Math.atan2( x, z ); - this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); + let mixFunction, + mixFunctionAdditive, + setIdentity; - } + // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + // + // 'add' is used for additive cumulative results + // + // 'work' is optional and is only present for quaternion types. It is used + // to store intermediate quaternion multiplication results - return this; + switch ( typeName ) { - } + case 'quaternion': + mixFunction = this._slerp; + mixFunctionAdditive = this._slerpAdditive; + setIdentity = this._setAdditiveIdentityQuaternion; - clone() { + this.buffer = new Float64Array( valueSize * 6 ); + this._workIndex = 5; + break; - return new this.constructor().copy( this ); + case 'string': + case 'bool': + mixFunction = this._select; - } + // Use the regular mix function and for additive on these types, + // additive is not relevant for non-numeric types + mixFunctionAdditive = this._select; -} + setIdentity = this._setAdditiveIdentityOther; -class ImmediateRenderObject extends Object3D { + this.buffer = new Array( valueSize * 5 ); + break; - constructor( material ) { + default: + mixFunction = this._lerp; + mixFunctionAdditive = this._lerpAdditive; + setIdentity = this._setAdditiveIdentityNumeric; - super(); + this.buffer = new Float64Array( valueSize * 5 ); - this.material = material; - this.render = function ( /* renderCallback */ ) {}; + } - this.hasPositions = false; - this.hasNormals = false; - this.hasColors = false; - this.hasUvs = false; + this._mixBufferRegion = mixFunction; + this._mixBufferRegionAdditive = mixFunctionAdditive; + this._setIdentity = setIdentity; + this._origIndex = 3; + this._addIndex = 4; - this.positionArray = null; - this.normalArray = null; - this.colorArray = null; - this.uvArray = null; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - this.count = 0; + this.useCount = 0; + this.referenceCount = 0; } -} + // accumulate data in the 'incoming' region into 'accu' + accumulate( accuIndex, weight ) { -ImmediateRenderObject.prototype.isImmediateRenderObject = true; + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place -const _vector$2 = /*@__PURE__*/ new Vector3(); -const _boneMatrix = /*@__PURE__*/ new Matrix4(); -const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); + const buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride; + let currentWeight = this.cumulativeWeight; -class SkeletonHelper extends LineSegments { + if ( currentWeight === 0 ) { - constructor( object ) { + // accuN := incoming * weight - const bones = getBoneList( object ); + for ( let i = 0; i !== stride; ++ i ) { - const geometry = new BufferGeometry(); + buffer[ offset + i ] = buffer[ i ]; - const vertices = []; - const colors = []; + } - const color1 = new Color( 0, 0, 1 ); - const color2 = new Color( 0, 1, 0 ); + currentWeight = weight; - for ( let i = 0; i < bones.length; i ++ ) { + } else { - const bone = bones[ i ]; + // accuN := accuN + incoming * weight - if ( bone.parent && bone.parent.isBone ) { + currentWeight += weight; + const mix = weight / currentWeight; + this._mixBufferRegion( buffer, offset, 0, mix, stride ); - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); + } - } + this.cumulativeWeight = currentWeight; - } + } - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + // accumulate data in the 'incoming' region into 'add' + accumulateAdditive( weight ) { - const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); + const buffer = this.buffer, + stride = this.valueSize, + offset = stride * this._addIndex; - super( geometry, material ); + if ( this.cumulativeWeightAdditive === 0 ) { - this.type = 'SkeletonHelper'; - this.isSkeletonHelper = true; + // add = identity - this.root = object; - this.bones = bones; + this._setIdentity(); - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; + } - } + // add := add + incoming * weight - updateMatrixWorld( force ) { + this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); + this.cumulativeWeightAdditive += weight; - const bones = this.bones; + } - const geometry = this.geometry; - const position = geometry.getAttribute( 'position' ); + // apply the state of 'accu' to the binding when accus differ + apply( accuIndex ) { - _matrixWorldInv.copy( this.root.matrixWorld ).invert(); + const stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, - for ( let i = 0, j = 0; i < bones.length; i ++ ) { + weight = this.cumulativeWeight, + weightAdditive = this.cumulativeWeightAdditive, - const bone = bones[ i ]; + binding = this.binding; - if ( bone.parent && bone.parent.isBone ) { + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); + if ( weight < 1 ) { - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); - _vector$2.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); + // accuN := accuN + original * ( 1 - cumulativeWeight ) - j += 2; + const originalValueOffset = stride * this._origIndex; - } + this._mixBufferRegion( + buffer, offset, originalValueOffset, 1 - weight, stride ); } - geometry.getAttribute( 'position' ).needsUpdate = true; + if ( weightAdditive > 0 ) { - super.updateMatrixWorld( force ); + // accuN := accuN + additive accuN - } + this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); -} + } + for ( let i = stride, e = stride + stride; i !== e; ++ i ) { -function getBoneList( object ) { + if ( buffer[ i ] !== buffer[ i + stride ] ) { - const boneList = []; + // value has changed -> update scene graph + + binding.setValue( buffer, offset ); + break; - if ( object && object.isBone ) { + } - boneList.push( object ); + } } - for ( let i = 0; i < object.children.length; i ++ ) { + // remember the state of the bound property and copy it to both accus + saveOriginalState() { - boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); + const binding = this.binding; - } + const buffer = this.buffer, + stride = this.valueSize, - return boneList; + originalValueOffset = stride * this._origIndex; -} + binding.getValue( buffer, originalValueOffset ); -class GridHelper extends LineSegments { + // accu[0..1] := orig -- initially detect changes against the original + for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { + buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - color1 = new Color( color1 ); - color2 = new Color( color2 ); + } - const center = divisions / 2; - const step = size / divisions; - const halfSize = size / 2; + // Add to identity for additive + this._setIdentity(); - const vertices = [], colors = []; + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { + } - vertices.push( - halfSize, 0, k, halfSize, 0, k ); - vertices.push( k, 0, - halfSize, k, 0, halfSize ); + // apply the state previously taken via 'saveOriginalState' to the binding + restoreOriginalState() { - const color = i === center ? color1 : color2; + const originalValueOffset = this.valueSize * 3; + this.binding.setValue( this.buffer, originalValueOffset ); - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; + } - } + _setAdditiveIdentityNumeric() { - const geometry = new BufferGeometry(); - geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + const startIndex = this._addIndex * this.valueSize; + const endIndex = startIndex + this.valueSize; - const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); + for ( let i = startIndex; i < endIndex; i ++ ) { - super( geometry, material ); + this.buffer[ i ] = 0; - this.type = 'GridHelper'; + } } -} + _setAdditiveIdentityQuaternion() { -const _floatView = new Float32Array( 1 ); -new Int32Array( _floatView.buffer ); + this._setAdditiveIdentityNumeric(); + this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; -const backgroundMaterial = new MeshBasicMaterial( { - side: BackSide, - depthWrite: false, - depthTest: false, -} ); -new Mesh( new BoxGeometry(), backgroundMaterial ); + } -// + _setAdditiveIdentityOther() { -Curve.create = function ( construct, getPoint ) { + const startIndex = this._origIndex * this.valueSize; + const targetIndex = this._addIndex * this.valueSize; - console.log( 'THREE.Curve.create() has been deprecated' ); + for ( let i = 0; i < this.valueSize; i ++ ) { - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; + this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - return construct; + } -}; + } -// -Path.prototype.fromPoints = function ( points ) { + // mix functions - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - return this.setFromPoints( points ); + _select( buffer, dstOffset, srcOffset, t, stride ) { -}; + if ( t >= 0.5 ) { -GridHelper.prototype.setColors = function () { + for ( let i = 0; i !== stride; ++ i ) { - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); + buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; -}; + } -SkeletonHelper.prototype.update = function () { + } - console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); + } -}; + _slerp( buffer, dstOffset, srcOffset, t ) { -// + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); -Loader.prototype.extractUrlBase = function ( url ) { + } - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); + _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { -}; + const workOffset = this._workIndex * stride; -Loader.Handlers = { + // Store result in intermediate buffer offset + Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - add: function ( /* regex, loader */ ) { + // Slerp to the intermediate result + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); + } - }, + _lerp( buffer, dstOffset, srcOffset, t, stride ) { - get: function ( /* file */ ) { + const s = 1 - t; - console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); + for ( let i = 0; i !== stride; ++ i ) { - } + const j = dstOffset + i; -}; + buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; -// + } -Box3.prototype.center = function ( optionalTarget ) { + } - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { -}; + for ( let i = 0; i !== stride; ++ i ) { -Box3.prototype.empty = function () { + const j = dstOffset + i; - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; -}; + } -Box3.prototype.isIntersectionBox = function ( box ) { + } - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); +} -}; +// Characters [].:/ are reserved for track binding syntax. +const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; +const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); -Box3.prototype.isIntersectionSphere = function ( sphere ) { +// Attempts to allow node names from any language. ES5's `\w` regexp matches +// only latin characters, and the unicode \p{L} is not yet supported. So +// instead, we exclude reserved characters and match everything else. +const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; +const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); +// Parent directories, delimited by '/' or ':'. Currently unused, but must +// be matched to parse the rest of the track name. +const _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); -}; +// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. +const _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); -Box3.prototype.size = function ( optionalTarget ) { +// Object on target node, and accessor. May not contain reserved +// characters. Accessor may contain any character except closing bracket. +const _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); +// Property and accessor. May not contain reserved characters. Accessor may +// contain any non-bracket characters. +const _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); -}; +const _trackRe = new RegExp( '' + + '^' + + _directoryRe + + _nodeRe + + _objectRe + + _propertyRe + + '$' +); -// +const _supportedObjectNames = [ 'material', 'materials', 'bones' ]; -Sphere.prototype.empty = function () { +class Composite { - console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + constructor( targetGroup, path, optionalParsedPath ) { -}; + const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); -// + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); -Frustum.prototype.setFromMatrix = function ( m ) { + } - console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); - return this.setFromProjectionMatrix( m ); + getValue( array, offset ) { -}; + this.bind(); // bind all binding -// + const firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[ firstValidIndex ]; -Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { + // and only call .getValue on the first + if ( binding !== undefined ) binding.getValue( array, offset ); - console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + } -}; + setValue( array, offset ) { -Matrix3.prototype.multiplyVector3 = function ( vector ) { + const bindings = this._bindings; - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { -}; + bindings[ i ].setValue( array, offset ); -Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { + } - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); + } -}; + bind() { -Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { + const bindings = this._bindings; - console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); - return attribute.applyMatrix3( this ); + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { -}; + bindings[ i ].bind(); -Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { + } - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); + } -}; + unbind() { -Matrix3.prototype.getInverse = function ( matrix ) { + const bindings = this._bindings; - console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { -}; + bindings[ i ].unbind(); -// + } -Matrix4.prototype.extractPosition = function ( m ) { + } - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); +} -}; +// Note: This class uses a State pattern on a per-method basis: +// 'bind' sets 'this.getValue' / 'setValue' and shadows the +// prototype version of these methods with one that represents +// the bound state. When the property is not found, the methods +// become no-ops. +class PropertyBinding { -Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { + constructor( rootNode, path, parsedPath ) { - console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); - return this.toArray( array, offset ); + this.path = path; + this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); -}; + this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; -Matrix4.prototype.getPosition = function () { + this.rootNode = rootNode; - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); + // initial state of these methods that calls 'bind' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; -}; + } -Matrix4.prototype.setRotationFromQuaternion = function ( q ) { - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); + static create( root, path, parsedPath ) { -}; + if ( ! ( root && root.isAnimationObjectGroup ) ) { -Matrix4.prototype.multiplyToArray = function () { + return new PropertyBinding( root, path, parsedPath ); - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); + } else { -}; + return new PropertyBinding.Composite( root, path, parsedPath ); -Matrix4.prototype.multiplyVector3 = function ( vector ) { + } - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + } -}; + /** + * Replaces spaces with underscores and removes unsupported characters from + * node names, to ensure compatibility with parseTrackName(). + * + * @param {string} name Node name to be sanitized. + * @return {string} + */ + static sanitizeNodeName( name ) { -Matrix4.prototype.multiplyVector4 = function ( vector ) { + return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + } -}; + static parseTrackName( trackName ) { -Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { + const matches = _trackRe.exec( trackName ); - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); + if ( matches === null ) { -}; + throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); -Matrix4.prototype.rotateAxis = function ( v ) { + } - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); + const results = { + // directoryName: matches[ 1 ], // (tschw) currently unused + nodeName: matches[ 2 ], + objectName: matches[ 3 ], + objectIndex: matches[ 4 ], + propertyName: matches[ 5 ], // required + propertyIndex: matches[ 6 ] + }; -}; + const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); -Matrix4.prototype.crossVector = function ( vector ) { + if ( lastDot !== undefined && lastDot !== - 1 ) { - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + const objectName = results.nodeName.substring( lastDot + 1 ); -}; + // Object names must be checked against an allowlist. Otherwise, there + // is no way to parse 'foo.bar.baz': 'baz' must be a property, but + // 'bar' could be the objectName, or part of a nodeName (which can + // include '.' characters). + if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { -Matrix4.prototype.translate = function () { + results.nodeName = results.nodeName.substring( 0, lastDot ); + results.objectName = objectName; - console.error( 'THREE.Matrix4: .translate() has been removed.' ); + } -}; + } -Matrix4.prototype.rotateX = function () { + if ( results.propertyName === null || results.propertyName.length === 0 ) { - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); -}; + } -Matrix4.prototype.rotateY = function () { + return results; - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + } -}; + static findNode( root, nodeName ) { -Matrix4.prototype.rotateZ = function () { + if ( nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + return root; -}; + } -Matrix4.prototype.rotateByAxis = function () { + // search into skeleton bones. + if ( root.skeleton ) { - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + const bone = root.skeleton.getBoneByName( nodeName ); -}; + if ( bone !== undefined ) { -Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { + return bone; - console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); - return attribute.applyMatrix4( this ); + } -}; + } -Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { + // search into node subtree. + if ( root.children ) { - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); + const searchNodeSubtree = function ( children ) { -}; + for ( let i = 0; i < children.length; i ++ ) { -Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { + const childNode = children[ i ]; - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { -}; + return childNode; -Matrix4.prototype.getInverse = function ( matrix ) { + } - console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); - return this.copy( matrix ).invert(); + const result = searchNodeSubtree( childNode.children ); -}; + if ( result ) return result; -// + } -Plane.prototype.isIntersectionLine = function ( line ) { + return null; - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); + }; -}; - -// + const subTreeNode = searchNodeSubtree( root.children ); -Quaternion.prototype.multiplyVector3 = function ( vector ) { + if ( subTreeNode ) { - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); + return subTreeNode; -}; + } -Quaternion.prototype.inverse = function ( ) { + } - console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); - return this.invert(); + return null; -}; + } -// + // these are used to "bind" a nonexistent property + _getValue_unavailable() {} + _setValue_unavailable() {} -Ray.prototype.isIntersectionBox = function ( box ) { + // Getters - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + _getValue_direct( buffer, offset ) { -}; + buffer[ offset ] = this.targetObject[ this.propertyName ]; -Ray.prototype.isIntersectionPlane = function ( plane ) { + } - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); + _getValue_array( buffer, offset ) { -}; + const source = this.resolvedProperty; -Ray.prototype.isIntersectionSphere = function ( sphere ) { + for ( let i = 0, n = source.length; i !== n; ++ i ) { - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); + buffer[ offset ++ ] = source[ i ]; -}; + } -// + } -Triangle.prototype.area = function () { + _getValue_arrayElement( buffer, offset ) { - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; -}; + } -Triangle.prototype.barycoordFromPoint = function ( point, target ) { + _getValue_toArray( buffer, offset ) { - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); + this.resolvedProperty.toArray( buffer, offset ); -}; + } -Triangle.prototype.midpoint = function ( target ) { + // Direct - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); + _setValue_direct( buffer, offset ) { -}; + this.targetObject[ this.propertyName ] = buffer[ offset ]; -Triangle.prototypenormal = function ( target ) { + } - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); + _setValue_direct_setNeedsUpdate( buffer, offset ) { -}; + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; -Triangle.prototype.plane = function ( target ) { + } - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); + _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { -}; + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; -Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { + } - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); + // EntireArray -}; + _setValue_array( buffer, offset ) { -Triangle.normal = function ( a, b, c, target ) { + const dest = this.resolvedProperty; - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); + for ( let i = 0, n = dest.length; i !== n; ++ i ) { -}; + dest[ i ] = buffer[ offset ++ ]; -// + } -Shape.prototype.extractAllPoints = function ( divisions ) { + } - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); + _setValue_array_setNeedsUpdate( buffer, offset ) { -}; + const dest = this.resolvedProperty; -Shape.prototype.extrude = function ( options ) { + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); + dest[ i ] = buffer[ offset ++ ]; -}; + } -Shape.prototype.makeGeometry = function ( options ) { + this.targetObject.needsUpdate = true; - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); + } -}; + _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { -// + const dest = this.resolvedProperty; -Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + dest[ i ] = buffer[ offset ++ ]; -}; + } -Vector2.prototype.distanceToManhattan = function ( v ) { + this.targetObject.matrixWorldNeedsUpdate = true; - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + } -}; + // ArrayElement -Vector2.prototype.lengthManhattan = function () { + _setValue_arrayElement( buffer, offset ) { - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; -}; + } -// + _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { -Vector3.prototype.setEulerFromRotationMatrix = function () { + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + } -}; + _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { -Vector3.prototype.setEulerFromQuaternion = function () { + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + } -}; + // HasToFromArray -Vector3.prototype.getPositionFromMatrix = function ( m ) { + _setValue_fromArray( buffer, offset ) { - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); + this.resolvedProperty.fromArray( buffer, offset ); -}; + } -Vector3.prototype.getScaleFromMatrix = function ( m ) { + _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; -}; + } -Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { + _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; -}; + } -Vector3.prototype.applyProjection = function ( m ) { + _getValue_unbound( targetArray, offset ) { - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); + this.bind(); + this.getValue( targetArray, offset ); -}; + } -Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { + _setValue_unbound( sourceArray, offset ) { - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + this.bind(); + this.setValue( sourceArray, offset ); -}; + } -Vector3.prototype.distanceToManhattan = function ( v ) { + // create getter / setter pair for a property in the scene graph + bind() { - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + let targetObject = this.node; + const parsedPath = this.parsedPath; -}; + const objectName = parsedPath.objectName; + const propertyName = parsedPath.propertyName; + let propertyIndex = parsedPath.propertyIndex; -Vector3.prototype.lengthManhattan = function () { + if ( ! targetObject ) { - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; -}; + this.node = targetObject; -// + } -Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { + // set fail state so we can just 'return' on error + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + // ensure there is a value node + if ( ! targetObject ) { -}; + console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); + return; -Vector4.prototype.lengthManhattan = function () { + } - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + if ( objectName ) { -}; + let objectIndex = parsedPath.objectIndex; -// + // special cases were we need to reach deeper into the hierarchy to get the face materials.... + switch ( objectName ) { -Object3D.prototype.getChildByName = function ( name ) { + case 'materials': - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); + if ( ! targetObject.material ) { -}; + console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); + return; -Object3D.prototype.renderDepth = function () { + } - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + if ( ! targetObject.material.materials ) { -}; + console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); + return; -Object3D.prototype.translate = function ( distance, axis ) { + } - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); + targetObject = targetObject.material.materials; -}; + break; -Object3D.prototype.getWorldRotation = function () { + case 'bones': - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); + if ( ! targetObject.skeleton ) { -}; + console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); + return; -Object3D.prototype.applyMatrix = function ( matrix ) { + } - console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. -}; + targetObject = targetObject.skeleton.bones; -Object.defineProperties( Object3D.prototype, { + // support resolving morphTarget names into indices. + for ( let i = 0; i < targetObject.length; i ++ ) { - eulerOrder: { - get: function () { + if ( targetObject[ i ].name === objectIndex ) { - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; + objectIndex = i; + break; - }, - set: function ( value ) { + } - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; + } - } - }, - useQuaternion: { - get: function () { + break; - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + default: - }, - set: function () { + if ( targetObject[ objectName ] === undefined ) { - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); + return; - } - } + } -} ); + targetObject = targetObject[ objectName ]; -Mesh.prototype.setDrawMode = function () { + } - console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); -}; + if ( objectIndex !== undefined ) { -Object.defineProperties( Mesh.prototype, { + if ( targetObject[ objectIndex ] === undefined ) { - drawMode: { - get: function () { + console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); + return; - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); - return TrianglesDrawMode; + } - }, - set: function () { + targetObject = targetObject[ objectIndex ]; - console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); + } } - } -} ); - -SkinnedMesh.prototype.initBones = function () { + // resolve property + const nodeProperty = targetObject[ propertyName ]; - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); + if ( nodeProperty === undefined ) { -}; + const nodeName = parsedPath.nodeName; -// + console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + + '.' + propertyName + ' but it wasn\'t found.', targetObject ); + return; -PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { + } - console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' + - 'Use .setFocalLength and .filmGauge for a photographic setup.' ); + // determine versioning scheme + let versioning = this.Versioning.None; - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); + this.targetObject = targetObject; -}; + if ( targetObject.needsUpdate !== undefined ) { // material -// + versioning = this.Versioning.NeedsUpdate; -Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + versioning = this.Versioning.MatrixWorldNeedsUpdate; } - }, - shadowCameraFov: { - set: function ( value ) { - - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; - } - }, - shadowCameraLeft: { - set: function ( value ) { + // determine how the property gets bound + let bindingType = this.BindingType.Direct; - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; + if ( propertyIndex !== undefined ) { - } - }, - shadowCameraRight: { - set: function ( value ) { + // access a sub element of the property array (only primitives are supported right now) - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; + if ( propertyName === 'morphTargetInfluences' ) { - } - }, - shadowCameraTop: { - set: function ( value ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { - } - }, - shadowCameraBottom: { - set: function ( value ) { + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); + return; - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; + } - } - }, - shadowCameraNear: { - set: function ( value ) { + if ( targetObject.geometry.isBufferGeometry ) { - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; + if ( ! targetObject.geometry.morphAttributes ) { - } - }, - shadowCameraFar: { - set: function ( value ) { + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); + return; - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; + } - } - }, - shadowCameraVisible: { - set: function () { + if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); + propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - } - }, - shadowBias: { - set: function ( value ) { + } - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; - } - }, - shadowDarkness: { - set: function () { + } else { - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences on THREE.Geometry. Use THREE.BufferGeometry instead.', this ); + return; - } - }, - shadowMapWidth: { - set: function ( value ) { + } - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; + } - } - }, - shadowMapHeight: { - set: function ( value ) { + bindingType = this.BindingType.ArrayElement; - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; - } - } -} ); + } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { -// + // must use copy for Object3D.Euler/Quaternion -Object.defineProperties( BufferAttribute.prototype, { + bindingType = this.BindingType.HasFromToArray; - length: { - get: function () { + this.resolvedProperty = nodeProperty; - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; + } else if ( Array.isArray( nodeProperty ) ) { - } - }, - dynamic: { - get: function () { + bindingType = this.BindingType.EntireArray; - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - return this.usage === DynamicDrawUsage; + this.resolvedProperty = nodeProperty; - }, - set: function ( /* value */ ) { + } else { - console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); - this.setUsage( DynamicDrawUsage ); + this.propertyName = propertyName; } - } - -} ); -BufferAttribute.prototype.setDynamic = function ( value ) { + // select getter / setter + this.getValue = this.GetterByBindingType[ bindingType ]; + this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; + } -}; + unbind() { -BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { + this.node = null; - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); + // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; -}, + } -BufferAttribute.prototype.setArray = function ( /* array */ ) { +} - console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); +PropertyBinding.Composite = Composite; -}; +PropertyBinding.prototype.BindingType = { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 +}; -// +PropertyBinding.prototype.Versioning = { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 +}; -BufferGeometry.prototype.addIndex = function ( index ) { +PropertyBinding.prototype.GetterByBindingType = [ - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); + PropertyBinding.prototype._getValue_direct, + PropertyBinding.prototype._getValue_array, + PropertyBinding.prototype._getValue_arrayElement, + PropertyBinding.prototype._getValue_toArray, -}; +]; -BufferGeometry.prototype.addAttribute = function ( name, attribute ) { +PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ - console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); + [ + // Direct + PropertyBinding.prototype._setValue_direct, + PropertyBinding.prototype._setValue_direct_setNeedsUpdate, + PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { + ], [ - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + // EntireArray - return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + PropertyBinding.prototype._setValue_array, + PropertyBinding.prototype._setValue_array_setNeedsUpdate, + PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, - } + ], [ - if ( name === 'index' ) { + // ArrayElement + PropertyBinding.prototype._setValue_arrayElement, + PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, + PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); + ], [ - return this; + // HasToFromArray + PropertyBinding.prototype._setValue_fromArray, + PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, + PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, - } + ] - return this.setAttribute( name, attribute ); +]; -}; +class AnimationAction { -BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { + constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { - if ( indexOffset !== undefined ) { + this._mixer = mixer; + this._clip = clip; + this._localRoot = localRoot; + this.blendMode = blendMode; - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + const tracks = clip.tracks, + nTracks = tracks.length, + interpolants = new Array( nTracks ); - } + const interpolantSettings = { + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + }; - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); + for ( let i = 0; i !== nTracks; ++ i ) { -}; + const interpolant = tracks[ i ].createInterpolant( null ); + interpolants[ i ] = interpolant; + interpolant.settings = interpolantSettings; -BufferGeometry.prototype.clearDrawCalls = function () { + } - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); + this._interpolantSettings = interpolantSettings; -}; + this._interpolants = interpolants; // bound by the mixer -BufferGeometry.prototype.computeOffsets = function () { + // inside: PropertyMixer (managed by the mixer) + this._propertyBindings = new Array( nTracks ); - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + this._cacheIndex = null; // for the memory manager + this._byClipCacheIndex = null; // for the memory manager -}; + this._timeScaleInterpolant = null; + this._weightInterpolant = null; -BufferGeometry.prototype.removeAttribute = function ( name ) { + this.loop = LoopRepeat; + this._loopCount = - 1; - console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); + // global mixer time when the action is to be started + // it's set back to 'null' upon start of the action + this._startTime = null; - return this.deleteAttribute( name ); + // scaled local time of the action + // gets clamped or wrapped to 0..clip.duration according to loop + this.time = 0; -}; + this.timeScale = 1; + this._effectiveTimeScale = 1; -BufferGeometry.prototype.applyMatrix = function ( matrix ) { + this.weight = 1; + this._effectiveWeight = 1; - console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); - return this.applyMatrix4( matrix ); + this.repetitions = Infinity; // no. of repetitions when looping -}; + this.paused = false; // true -> zero effective time scale + this.enabled = true; // false -> zero effective weight -Object.defineProperties( BufferGeometry.prototype, { + this.clampWhenFinished = false;// keep feeding the last frame? - drawcalls: { - get: function () { + this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate + this.zeroSlopeAtEnd = true;// clips for start, loop and end - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; + } - } - }, - offsets: { - get: function () { + // State & Scheduling - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; + play() { - } - } + this._mixer._activateAction( this ); -} ); + return this; -InterleavedBuffer.prototype.setDynamic = function ( value ) { + } - console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); - this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); - return this; + stop() { -}; + this._mixer._deactivateAction( this ); -InterleavedBuffer.prototype.setArray = function ( /* array */ ) { + return this.reset(); - console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); + } -}; + reset() { -// + this.paused = false; + this.enabled = true; -ExtrudeGeometry.prototype.getArrays = function () { + this.time = 0; // restart clip + this._loopCount = - 1;// forget previous loops + this._startTime = null;// forget scheduling - console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); + return this.stopFading().stopWarping(); -}; + } -ExtrudeGeometry.prototype.addShapeList = function () { + isRunning() { - console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); + return this.enabled && ! this.paused && this.timeScale !== 0 && + this._startTime === null && this._mixer._isActiveAction( this ); -}; + } -ExtrudeGeometry.prototype.addShape = function () { + // return true when play has been called + isScheduled() { - console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); + return this._mixer._isActiveAction( this ); -}; + } -// + startAt( time ) { -Scene.prototype.dispose = function () { + this._startTime = time; - console.error( 'THREE.Scene: .dispose() has been removed.' ); + return this; -}; + } -// + setLoop( mode, repetitions ) { -Object.defineProperties( Material.prototype, { + this.loop = mode; + this.repetitions = repetitions; - wrapAround: { - get: function () { + return this; - console.warn( 'THREE.Material: .wrapAround has been removed.' ); + } - }, - set: function () { + // Weight - console.warn( 'THREE.Material: .wrapAround has been removed.' ); + // set the weight stopping any scheduled fading + // although .enabled = false yields an effective weight of zero, this + // method does *not* change .enabled, because it would be confusing + setEffectiveWeight( weight ) { - } - }, + this.weight = weight; - overdraw: { - get: function () { + // note: same logic as when updated at runtime + this._effectiveWeight = this.enabled ? weight : 0; - console.warn( 'THREE.Material: .overdraw has been removed.' ); + return this.stopFading(); - }, - set: function () { + } - console.warn( 'THREE.Material: .overdraw has been removed.' ); + // return the weight considering fading and .enabled + getEffectiveWeight() { - } - }, + return this._effectiveWeight; - wrapRGB: { - get: function () { + } - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); + fadeIn( duration ) { - } - }, + return this._scheduleFading( duration, 0, 1 ); - shading: { - get: function () { + } - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + fadeOut( duration ) { - }, - set: function ( value ) { + return this._scheduleFading( duration, 1, 0 ); - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); + } - } - }, + crossFadeFrom( fadeOutAction, duration, warp ) { - stencilMask: { - get: function () { + fadeOutAction.fadeOut( duration ); + this.fadeIn( duration ); - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - return this.stencilFuncMask; + if ( warp ) { - }, - set: function ( value ) { + const fadeInDuration = this._clip.duration, + fadeOutDuration = fadeOutAction._clip.duration, - console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); - this.stencilFuncMask = value; + startEndRatio = fadeOutDuration / fadeInDuration, + endStartRatio = fadeInDuration / fadeOutDuration; + + fadeOutAction.warp( 1.0, startEndRatio, duration ); + this.warp( endStartRatio, 1.0, duration ); } + + return this; + } -} ); + crossFadeTo( fadeInAction, duration, warp ) { -Object.defineProperties( ShaderMaterial.prototype, { + return fadeInAction.crossFadeFrom( this, duration, warp ); - derivatives: { - get: function () { + } - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; + stopFading() { - }, - set: function ( value ) { + const weightInterpolant = this._weightInterpolant; - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; + if ( weightInterpolant !== null ) { + + this._weightInterpolant = null; + this._mixer._takeBackControlInterpolant( weightInterpolant ); } - } -} ); + return this; -// + } -WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { + // Time Scale Control - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + // set the time scale stopping any scheduled warping + // although .paused = true yields an effective time scale of zero, this + // method does *not* change .paused, because it would be confusing + setEffectiveTimeScale( timeScale ) { -}; + this.timeScale = timeScale; + this._effectiveTimeScale = this.paused ? 0 : timeScale; -WebGLRenderer.prototype.animate = function ( callback ) { + return this.stopWarping(); - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); + } -}; + // return the time scale considering warping and .paused + getEffectiveTimeScale() { -WebGLRenderer.prototype.getCurrentRenderTarget = function () { + return this._effectiveTimeScale; - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); + } -}; + setDuration( duration ) { -WebGLRenderer.prototype.getMaxAnisotropy = function () { + this.timeScale = this._clip.duration / duration; - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); + return this.stopWarping(); -}; + } -WebGLRenderer.prototype.getPrecision = function () { + syncWith( action ) { - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; + this.time = action.time; + this.timeScale = action.timeScale; -}; + return this.stopWarping(); -WebGLRenderer.prototype.resetGLState = function () { + } - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); + halt( duration ) { -}; + return this.warp( this._effectiveTimeScale, 0, duration ); -WebGLRenderer.prototype.supportsFloatTextures = function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); + warp( startTimeScale, endTimeScale, duration ) { -}; + const mixer = this._mixer, + now = mixer.time, + timeScale = this.timeScale; -WebGLRenderer.prototype.supportsHalfFloatTextures = function () { + let interpolant = this._timeScaleInterpolant; - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); + if ( interpolant === null ) { -}; + interpolant = mixer._lendControlInterpolant(); + this._timeScaleInterpolant = interpolant; -WebGLRenderer.prototype.supportsStandardDerivatives = function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; -}; + times[ 0 ] = now; + times[ 1 ] = now + duration; -WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { + values[ 0 ] = startTimeScale / timeScale; + values[ 1 ] = endTimeScale / timeScale; - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); + return this; -}; + } -WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { + stopWarping() { - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + const timeScaleInterpolant = this._timeScaleInterpolant; -}; + if ( timeScaleInterpolant !== null ) { -WebGLRenderer.prototype.supportsBlendMinMax = function () { + this._timeScaleInterpolant = null; + this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); + } -}; + return this; -WebGLRenderer.prototype.supportsVertexTextures = function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; + // Object Accessors -}; + getMixer() { -WebGLRenderer.prototype.supportsInstancedArrays = function () { + return this._mixer; - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); + } -}; + getClip() { -WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { + return this._clip; - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); + } -}; + getRoot() { -WebGLRenderer.prototype.initMaterial = function () { + return this._localRoot || this._mixer._root; - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + } -}; + // Interna -WebGLRenderer.prototype.addPrePlugin = function () { + _update( time, deltaTime, timeDirection, accuIndex ) { - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + // called by the mixer -}; + if ( ! this.enabled ) { -WebGLRenderer.prototype.addPostPlugin = function () { + // call ._updateWeight() to update ._effectiveWeight - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + this._updateWeight( time ); + return; -}; + } -WebGLRenderer.prototype.updateShadowMap = function () { + const startTime = this._startTime; - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + if ( startTime !== null ) { -}; + // check for scheduled start of action -WebGLRenderer.prototype.setFaceCulling = function () { + const timeRunning = ( time - startTime ) * timeDirection; + if ( timeRunning < 0 || timeDirection === 0 ) { - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + return; // yet to come / don't decide when delta = 0 -}; + } -WebGLRenderer.prototype.allocTextureUnit = function () { + // start - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + this._startTime = null; // unschedule + deltaTime = timeDirection * timeRunning; -}; + } -WebGLRenderer.prototype.setTexture = function () { + // apply time scale and advance time - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + deltaTime *= this._updateTimeScale( time ); + const clipTime = this._updateTime( deltaTime ); -}; + // note: _updateTime may disable the action resulting in + // an effective weight of 0 -WebGLRenderer.prototype.setTexture2D = function () { + const weight = this._updateWeight( time ); - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + if ( weight > 0 ) { -}; + const interpolants = this._interpolants; + const propertyMixers = this._propertyBindings; -WebGLRenderer.prototype.setTextureCube = function () { + switch ( this.blendMode ) { - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + case AdditiveAnimationBlendMode: -}; + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { -WebGLRenderer.prototype.getActiveMipMapLevel = function () { + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulateAdditive( weight ); - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); + } -}; + break; -Object.defineProperties( WebGLRenderer.prototype, { + case NormalAnimationBlendMode: + default: - shadowMapEnabled: { - get: function () { + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - return this.shadowMap.enabled; + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulate( accuIndex, weight ); - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; + } } - }, - shadowMapType: { - get: function () { - return this.shadowMap.type; + } - }, - set: function ( value ) { + _updateWeight( time ) { - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; + let weight = 0; - } - }, - shadowMapCullFace: { - get: function () { + if ( this.enabled ) { - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + weight = this.weight; + const interpolant = this._weightInterpolant; - }, - set: function ( /* value */ ) { + if ( interpolant !== null ) { - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - } - }, - context: { - get: function () { + weight *= interpolantValue; - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); + if ( time > interpolant.parameterPositions[ 1 ] ) { - } - }, - vr: { - get: function () { + this.stopFading(); - console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); - return this.xr; + if ( interpolantValue === 0 ) { - } - }, - gammaInput: { - get: function () { + // faded out, disable + this.enabled = false; - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - return false; + } - }, - set: function () { + } - console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); + } } - }, - gammaOutput: { - get: function () { - - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - return false; - }, - set: function ( value ) { + this._effectiveWeight = weight; + return weight; - console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); - this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; + } - } - }, - toneMappingWhitePoint: { - get: function () { + _updateTimeScale( time ) { - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - return 1.0; + let timeScale = 0; - }, - set: function () { + if ( ! this.paused ) { - console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); + timeScale = this.timeScale; - } - }, + const interpolant = this._timeScaleInterpolant; -} ); + if ( interpolant !== null ) { -Object.defineProperties( WebGLShadowMap.prototype, { + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - cullFace: { - get: function () { + timeScale *= interpolantValue; - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + if ( time > interpolant.parameterPositions[ 1 ] ) { - }, - set: function ( /* cullFace */ ) { + this.stopWarping(); - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); + if ( timeScale === 0 ) { - } - }, - renderReverseSided: { - get: function () { + // motion has halted, pause + this.paused = true; - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; + } else { - }, - set: function () { + // warp done - apply final time scale + this.timeScale = timeScale; - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); + } - } - }, - renderSingleSided: { - get: function () { + } - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; + } - }, - set: function () { + } - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); + this._effectiveTimeScale = timeScale; + return timeScale; - } } -} ); + _updateTime( deltaTime ) { -// + const duration = this._clip.duration; + const loop = this.loop; -Object.defineProperties( WebGLRenderTarget.prototype, { + let time = this.time + deltaTime; + let loopCount = this._loopCount; - wrapS: { - get: function () { + const pingPong = ( loop === LoopPingPong ); - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; + if ( deltaTime === 0 ) { - }, - set: function ( value ) { + if ( loopCount === - 1 ) return time; - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; + return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; } - }, - wrapT: { - get: function () { - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; + if ( loop === LoopOnce ) { - }, - set: function ( value ) { + if ( loopCount === - 1 ) { - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; + // just started - } - }, - magFilter: { - get: function () { + this._loopCount = 0; + this._setEndings( true, true, false ); - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; + } - }, - set: function ( value ) { + handle_stop: { - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; + if ( time >= duration ) { - } - }, - minFilter: { - get: function () { + time = duration; - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; + } else if ( time < 0 ) { - }, - set: function ( value ) { + time = 0; - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; + } else { - } - }, - anisotropy: { - get: function () { + this.time = time; - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; + break handle_stop; - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; - } - }, - offset: { - get: function () { + this.time = time; - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime < 0 ? - 1 : 1 + } ); - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; + } else { // repetitive Repeat or PingPong - } - }, - repeat: { - get: function () { + if ( loopCount === - 1 ) { - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; + // just started - }, - set: function ( value ) { + if ( deltaTime >= 0 ) { - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; + loopCount = 0; - } - }, - format: { - get: function () { + this._setEndings( true, this.repetitions === 0, pingPong ); - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; + } else { - }, - set: function ( value ) { + // when looping in reverse direction, the initial + // transition through zero counts as a repetition, + // so leave loopCount at -1 - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; + this._setEndings( this.repetitions === 0, true, pingPong ); - } - }, - type: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; + } - }, - set: function ( value ) { + if ( time >= duration || time < 0 ) { - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; + // wrap around - } - }, - generateMipmaps: { - get: function () { + const loopDelta = Math.floor( time / duration ); // signed + time -= duration * loopDelta; - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; + loopCount += Math.abs( loopDelta ); - }, - set: function ( value ) { + const pending = this.repetitions - loopCount; - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; + if ( pending <= 0 ) { - } - } + // have to stop (switch state, clamp time, fire event) -} ); + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; -// + time = deltaTime > 0 ? duration : 0; -Audio.prototype.load = function ( file ) { + this.time = time; - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - const scope = this; - const audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime > 0 ? 1 : - 1 + } ); - scope.setBuffer( buffer ); + } else { - } ); - return this; + // keep running -}; + if ( pending === 1 ) { -// + // entering the last round -CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { + const atStart = deltaTime < 0; + this._setEndings( atStart, ! atStart, pingPong ); - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); + } else { -}; + this._setEndings( false, false, pingPong ); -CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) { + } - console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' ); - return this.renderTarget.clear( renderer, color, depth, stencil ); + this._loopCount = loopCount; -}; + this.time = time; -ImageUtils.crossOrigin = undefined; + this._mixer.dispatchEvent( { + type: 'loop', action: this, loopDelta: loopDelta + } ); -ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { + } - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); + } else { - const loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + this.time = time; - const texture = loader.load( url, onLoad, undefined, onError ); + } - if ( mapping ) texture.mapping = mapping; + if ( pingPong && ( loopCount & 1 ) === 1 ) { - return texture; + // invert time for the "pong round" -}; + return duration - time; -ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { + } - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); + } - const loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + return time; - const texture = loader.load( urls, onLoad, undefined, onError ); + } - if ( mapping ) texture.mapping = mapping; + _setEndings( atStart, atEnd, pingPong ) { - return texture; + const settings = this._interpolantSettings; -}; + if ( pingPong ) { -ImageUtils.loadCompressedTexture = function () { + settings.endingStart = ZeroSlopeEnding; + settings.endingEnd = ZeroSlopeEnding; - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); + } else { -}; + // assuming for LoopOnce atStart == atEnd == true -ImageUtils.loadCompressedTextureCube = function () { + if ( atStart ) { - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; -}; + } else { -if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + settings.endingStart = WrapAroundEnding; - /* eslint-disable no-undef */ - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { - revision: REVISION, - } } ) ); - /* eslint-enable no-undef */ + } -} + if ( atEnd ) { -if ( typeof window !== 'undefined' ) { + settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - if ( window.__THREE__ ) { + } else { - console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); + settings.endingEnd = WrapAroundEnding; - } else { + } - window.__THREE__ = REVISION; + } } -} + _scheduleFading( duration, weightNow, weightThen ) { -const _taskCache = new WeakMap(); + const mixer = this._mixer, now = mixer.time; + let interpolant = this._weightInterpolant; -class DRACOLoader extends Loader { + if ( interpolant === null ) { - constructor( manager ) { + interpolant = mixer._lendControlInterpolant(); + this._weightInterpolant = interpolant; - super( manager ); + } - this.decoderPath = ''; - this.decoderConfig = {}; - this.decoderBinary = null; - this.decoderPending = null; + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; - this.workerLimit = 4; - this.workerPool = []; - this.workerNextTaskID = 1; - this.workerSourceURL = ''; + times[ 0 ] = now; + values[ 0 ] = weightNow; + times[ 1 ] = now + duration; + values[ 1 ] = weightThen; - this.defaultAttributeIDs = { - position: 'POSITION', - normal: 'NORMAL', - color: 'COLOR', - uv: 'TEX_COORD' - }; - this.defaultAttributeTypes = { - position: 'Float32Array', - normal: 'Float32Array', - color: 'Float32Array', - uv: 'Float32Array' - }; + return this; } - setDecoderPath( path ) { +} - this.decoderPath = path; +class AnimationMixer extends EventDispatcher$1 { - return this; + constructor( root ) { - } + super(); - setDecoderConfig( config ) { + this._root = root; + this._initMemoryManager(); + this._accuIndex = 0; + this.time = 0; + this.timeScale = 1.0; - this.decoderConfig = config; + } - return this; + _bindAction( action, prototypeAction ) { - } + const root = action._localRoot || this._root, + tracks = action._clip.tracks, + nTracks = tracks.length, + bindings = action._propertyBindings, + interpolants = action._interpolants, + rootUuid = root.uuid, + bindingsByRoot = this._bindingsByRootAndName; - setWorkerLimit( workerLimit ) { + let bindingsByName = bindingsByRoot[ rootUuid ]; - this.workerLimit = workerLimit; + if ( bindingsByName === undefined ) { - return this; + bindingsByName = {}; + bindingsByRoot[ rootUuid ] = bindingsByName; - } + } - load( url, onLoad, onProgress, onError ) { + for ( let i = 0; i !== nTracks; ++ i ) { - const loader = new FileLoader( this.manager ); + const track = tracks[ i ], + trackName = track.name; - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); + let binding = bindingsByName[ trackName ]; - loader.load( url, ( buffer ) => { + if ( binding !== undefined ) { - const taskConfig = { - attributeIDs: this.defaultAttributeIDs, - attributeTypes: this.defaultAttributeTypes, - useUniqueIDs: false - }; + ++ binding.referenceCount; + bindings[ i ] = binding; - this.decodeGeometry( buffer, taskConfig ) - .then( onLoad ) - .catch( onError ); + } else { - }, onProgress, onError ); + binding = bindings[ i ]; - } + if ( binding !== undefined ) { - /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ - decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) { + // existing binding, make sure the cache knows - const taskConfig = { - attributeIDs: attributeIDs || this.defaultAttributeIDs, - attributeTypes: attributeTypes || this.defaultAttributeTypes, - useUniqueIDs: !! attributeIDs - }; + if ( binding._cacheIndex === null ) { - this.decodeGeometry( buffer, taskConfig ).then( callback ); + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); - } + } - decodeGeometry( buffer, taskConfig ) { + continue; - // TODO: For backward-compatibility, support 'attributeTypes' objects containing - // references (rather than names) to typed array constructors. These must be - // serialized before sending them to the worker. - for ( const attribute in taskConfig.attributeTypes ) { + } - const type = taskConfig.attributeTypes[ attribute ]; + const path = prototypeAction && prototypeAction. + _propertyBindings[ i ].binding.parsedPath; - if ( type.BYTES_PER_ELEMENT !== undefined ) { + binding = new PropertyMixer( + PropertyBinding.create( root, trackName, path ), + track.ValueTypeName, track.getValueSize() ); - taskConfig.attributeTypes[ attribute ] = type.name; + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); + + bindings[ i ] = binding; } + interpolants[ i ].resultBuffer = binding.buffer; + } - // + } - const taskKey = JSON.stringify( taskConfig ); + _activateAction( action ) { - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if ( _taskCache.has( buffer ) ) { + if ( ! this._isActiveAction( action ) ) { - const cachedTask = _taskCache.get( buffer ); + if ( action._cacheIndex === null ) { - if ( cachedTask.key === taskKey ) { + // this action has been forgotten by the cache, but the user + // appears to be still using it -> rebind - return cachedTask.promise; + const rootUuid = ( action._localRoot || this._root ).uuid, + clipUuid = action._clip.uuid, + actionsForClip = this._actionsByClip[ clipUuid ]; - } else if ( buffer.byteLength === 0 ) { + this._bindAction( action, + actionsForClip && actionsForClip.knownActions[ 0 ] ); - // Technically, it would be possible to wait for the previous task to complete, - // transfer the buffer back, and decode again with the second configuration. That - // is complex, and I don't know of any reason to decode a Draco buffer twice in - // different ways, so this is left unimplemented. - throw new Error( + this._addInactiveAction( action, clipUuid, rootUuid ); - 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + - 'settings. Buffer has already been transferred.' + } - ); + const bindings = action._propertyBindings; + + // increment reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { + + const binding = bindings[ i ]; + + if ( binding.useCount ++ === 0 ) { + + this._lendBinding( binding ); + binding.saveOriginalState(); + + } } + this._lendAction( action ); + } - // + } - let worker; - const taskID = this.workerNextTaskID ++; - const taskCost = buffer.byteLength; + _deactivateAction( action ) { - // Obtain a worker and assign a task, and construct a geometry instance - // when the task completes. - const geometryPending = this._getWorker( taskID, taskCost ) - .then( ( _worker ) => { + if ( this._isActiveAction( action ) ) { - worker = _worker; + const bindings = action._propertyBindings; - return new Promise( ( resolve, reject ) => { + // decrement reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - worker._callbacks[ taskID ] = { resolve, reject }; + const binding = bindings[ i ]; - worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); + if ( -- binding.useCount === 0 ) { - // this.debug(); + binding.restoreOriginalState(); + this._takeBackBinding( binding ); - } ); + } - } ) - .then( ( message ) => this._createGeometry( message.geometry ) ); + } - // Remove task from the task list. - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - geometryPending - .catch( () => true ) - .then( () => { + this._takeBackAction( action ); - if ( worker && taskID ) { + } - this._releaseTask( worker, taskID ); + } - // this.debug(); + // Memory manager - } + _initMemoryManager() { - } ); + this._actions = []; // 'nActiveActions' followed by inactive ones + this._nActiveActions = 0; - // Cache the task result. - _taskCache.set( buffer, { + this._actionsByClip = {}; + // inside: + // { + // knownActions: Array< AnimationAction > - used as prototypes + // actionByRoot: AnimationAction - lookup + // } - key: taskKey, - promise: geometryPending - } ); + this._bindings = []; // 'nActiveBindings' followed by inactive ones + this._nActiveBindings = 0; - return geometryPending; + this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - } - _createGeometry( geometryData ) { + this._controlInterpolants = []; // same game as above + this._nActiveControlInterpolants = 0; - const geometry = new BufferGeometry(); + const scope = this; - if ( geometryData.index ) { + this.stats = { - geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); + actions: { + get total() { - } + return scope._actions.length; - for ( let i = 0; i < geometryData.attributes.length; i ++ ) { + }, + get inUse() { - const attribute = geometryData.attributes[ i ]; - const name = attribute.name; - const array = attribute.array; - const itemSize = attribute.itemSize; + return scope._nActiveActions; - geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); + } + }, + bindings: { + get total() { - } + return scope._bindings.length; - return geometry; + }, + get inUse() { - } + return scope._nActiveBindings; - _loadLibrary( url, responseType ) { + } + }, + controlInterpolants: { + get total() { - const loader = new FileLoader( this.manager ); - loader.setPath( this.decoderPath ); - loader.setResponseType( responseType ); - loader.setWithCredentials( this.withCredentials ); + return scope._controlInterpolants.length; - return new Promise( ( resolve, reject ) => { + }, + get inUse() { - loader.load( url, resolve, undefined, reject ); + return scope._nActiveControlInterpolants; - } ); + } + } + + }; } - preload() { + // Memory management for AnimationAction objects - this._initDecoder(); + _isActiveAction( action ) { - return this; + const index = action._cacheIndex; + return index !== null && index < this._nActiveActions; } - _initDecoder() { + _addInactiveAction( action, clipUuid, rootUuid ) { - if ( this.decoderPending ) return this.decoderPending; + const actions = this._actions, + actionsByClip = this._actionsByClip; - const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; - const librariesPending = []; + let actionsForClip = actionsByClip[ clipUuid ]; - if ( useJS ) { + if ( actionsForClip === undefined ) { - librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); + actionsForClip = { - } else { + knownActions: [ action ], + actionByRoot: {} - librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); - librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); + }; - } + action._byClipCacheIndex = 0; - this.decoderPending = Promise.all( librariesPending ) - .then( ( libraries ) => { + actionsByClip[ clipUuid ] = actionsForClip; - const jsContent = libraries[ 0 ]; + } else { - if ( ! useJS ) { + const knownActions = actionsForClip.knownActions; - this.decoderConfig.wasmBinary = libraries[ 1 ]; + action._byClipCacheIndex = knownActions.length; + knownActions.push( action ); - } + } - const fn = DRACOWorker.toString(); + action._cacheIndex = actions.length; + actions.push( action ); - const body = [ - '/* draco decoder */', - jsContent, - '', - '/* worker */', - fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) - ].join( '\n' ); + actionsForClip.actionByRoot[ rootUuid ] = action; - this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); + } - } ); + _removeInactiveAction( action ) { - return this.decoderPending; + const actions = this._actions, + lastInactiveAction = actions[ actions.length - 1 ], + cacheIndex = action._cacheIndex; - } + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); - _getWorker( taskID, taskCost ) { + action._cacheIndex = null; - return this._initDecoder().then( () => { - if ( this.workerPool.length < this.workerLimit ) { + const clipUuid = action._clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ], + knownActionsForClip = actionsForClip.knownActions, - const worker = new Worker( this.workerSourceURL ); + lastKnownAction = + knownActionsForClip[ knownActionsForClip.length - 1 ], - worker._callbacks = {}; - worker._taskCosts = {}; - worker._taskLoad = 0; + byClipCacheIndex = action._byClipCacheIndex; - worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); + lastKnownAction._byClipCacheIndex = byClipCacheIndex; + knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; + knownActionsForClip.pop(); - worker.onmessage = function ( e ) { + action._byClipCacheIndex = null; - const message = e.data; - switch ( message.type ) { + const actionByRoot = actionsForClip.actionByRoot, + rootUuid = ( action._localRoot || this._root ).uuid; - case 'decode': - worker._callbacks[ message.id ].resolve( message ); - break; + delete actionByRoot[ rootUuid ]; - case 'error': - worker._callbacks[ message.id ].reject( message ); - break; + if ( knownActionsForClip.length === 0 ) { - default: - console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); + delete actionsByClip[ clipUuid ]; - } + } - }; + this._removeInactiveBindingsForAction( action ); - this.workerPool.push( worker ); + } - } else { + _removeInactiveBindingsForAction( action ) { - this.workerPool.sort( function ( a, b ) { + const bindings = action._propertyBindings; - return a._taskLoad > b._taskLoad ? - 1 : 1; + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - } ); + const binding = bindings[ i ]; - } + if ( -- binding.referenceCount === 0 ) { - const worker = this.workerPool[ this.workerPool.length - 1 ]; - worker._taskCosts[ taskID ] = taskCost; - worker._taskLoad += taskCost; - return worker; + this._removeInactiveBinding( binding ); - } ); + } + + } } - _releaseTask( worker, taskID ) { + _lendAction( action ) { - worker._taskLoad -= worker._taskCosts[ taskID ]; - delete worker._callbacks[ taskID ]; - delete worker._taskCosts[ taskID ]; + // [ active actions | inactive actions ] + // [ active actions >| inactive actions ] + // s a + // <-swap-> + // a s - } + const actions = this._actions, + prevIndex = action._cacheIndex, - debug() { + lastActiveIndex = this._nActiveActions ++, - console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); + firstInactiveAction = actions[ lastActiveIndex ]; + + action._cacheIndex = lastActiveIndex; + actions[ lastActiveIndex ] = action; + + firstInactiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = firstInactiveAction; } - dispose() { + _takeBackAction( action ) { - for ( let i = 0; i < this.workerPool.length; ++ i ) { + // [ active actions | inactive actions ] + // [ active actions |< inactive actions ] + // a s + // <-swap-> + // s a - this.workerPool[ i ].terminate(); + const actions = this._actions, + prevIndex = action._cacheIndex, - } + firstInactiveIndex = -- this._nActiveActions, - this.workerPool.length = 0; + lastActiveAction = actions[ firstInactiveIndex ]; - return this; + action._cacheIndex = firstInactiveIndex; + actions[ firstInactiveIndex ] = action; - } + lastActiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = lastActiveAction; -} + } -/* WEB WORKER */ + // Memory management for PropertyMixer objects -function DRACOWorker() { + _addInactiveBinding( binding, rootUuid, trackName ) { - let decoderConfig; - let decoderPending; + const bindingsByRoot = this._bindingsByRootAndName, + bindings = this._bindings; - onmessage = function ( e ) { + let bindingByName = bindingsByRoot[ rootUuid ]; - const message = e.data; + if ( bindingByName === undefined ) { - switch ( message.type ) { + bindingByName = {}; + bindingsByRoot[ rootUuid ] = bindingByName; - case 'init': - decoderConfig = message.decoderConfig; - decoderPending = new Promise( function ( resolve/*, reject*/ ) { + } - decoderConfig.onModuleLoaded = function ( draco ) { + bindingByName[ trackName ] = binding; - // Module is Promise-like. Wrap before resolving to avoid loop. - resolve( { draco: draco } ); + binding._cacheIndex = bindings.length; + bindings.push( binding ); - }; + } - DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef + _removeInactiveBinding( binding ) { - } ); - break; + const bindings = this._bindings, + propBinding = binding.binding, + rootUuid = propBinding.rootNode.uuid, + trackName = propBinding.path, + bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ], - case 'decode': - const buffer = message.buffer; - const taskConfig = message.taskConfig; - decoderPending.then( ( module ) => { + lastInactiveBinding = bindings[ bindings.length - 1 ], + cacheIndex = binding._cacheIndex; - const draco = module.draco; - const decoder = new draco.Decoder(); - const decoderBuffer = new draco.DecoderBuffer(); - decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); + lastInactiveBinding._cacheIndex = cacheIndex; + bindings[ cacheIndex ] = lastInactiveBinding; + bindings.pop(); - try { + delete bindingByName[ trackName ]; - const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); + if ( Object.keys( bindingByName ).length === 0 ) { - const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); + delete bindingsByRoot[ rootUuid ]; - if ( geometry.index ) buffers.push( geometry.index.array.buffer ); + } - self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); + } - } catch ( error ) { + _lendBinding( binding ) { - console.error( error ); + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - self.postMessage( { type: 'error', id: message.id, error: error.message } ); + lastActiveIndex = this._nActiveBindings ++, - } finally { + firstInactiveBinding = bindings[ lastActiveIndex ]; - draco.destroy( decoderBuffer ); - draco.destroy( decoder ); + binding._cacheIndex = lastActiveIndex; + bindings[ lastActiveIndex ] = binding; - } + firstInactiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = firstInactiveBinding; - } ); - break; + } - } + _takeBackBinding( binding ) { - }; + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { + firstInactiveIndex = -- this._nActiveBindings, - const attributeIDs = taskConfig.attributeIDs; - const attributeTypes = taskConfig.attributeTypes; + lastActiveBinding = bindings[ firstInactiveIndex ]; - let dracoGeometry; - let decodingStatus; + binding._cacheIndex = firstInactiveIndex; + bindings[ firstInactiveIndex ] = binding; - const geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); + lastActiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = lastActiveBinding; - if ( geometryType === draco.TRIANGULAR_MESH ) { + } - dracoGeometry = new draco.Mesh(); - decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - } else if ( geometryType === draco.POINT_CLOUD ) { + // Memory management of Interpolants for weight and time scale - dracoGeometry = new draco.PointCloud(); - decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); + _lendControlInterpolant() { - } else { + const interpolants = this._controlInterpolants, + lastActiveIndex = this._nActiveControlInterpolants ++; - throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); + let interpolant = interpolants[ lastActiveIndex ]; - } + if ( interpolant === undefined ) { - if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { + interpolant = new LinearInterpolant( + new Float32Array( 2 ), new Float32Array( 2 ), + 1, this._controlInterpolantsResultBuffer ); - throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); + interpolant.__cacheIndex = lastActiveIndex; + interpolants[ lastActiveIndex ] = interpolant; } - const geometry = { index: null, attributes: [] }; + return interpolant; - // Gather all vertex attributes. - for ( const attributeName in attributeIDs ) { + } - const attributeType = self[ attributeTypes[ attributeName ] ]; + _takeBackControlInterpolant( interpolant ) { - let attribute; - let attributeID; + const interpolants = this._controlInterpolants, + prevIndex = interpolant.__cacheIndex, - // A Draco file may be created with default vertex attributes, whose attribute IDs - // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, - // a Draco file may contain a custom set of attributes, identified by known unique - // IDs. glTF files always do the latter, and `.drc` files typically do the former. - if ( taskConfig.useUniqueIDs ) { + firstInactiveIndex = -- this._nActiveControlInterpolants, - attributeID = attributeIDs[ attributeName ]; - attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); + lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - } else { + interpolant.__cacheIndex = firstInactiveIndex; + interpolants[ firstInactiveIndex ] = interpolant; - attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); + lastActiveInterpolant.__cacheIndex = prevIndex; + interpolants[ prevIndex ] = lastActiveInterpolant; - if ( attributeID === - 1 ) continue; + } - attribute = decoder.GetAttribute( dracoGeometry, attributeID ); + // return an action for a clip optionally using a custom root target + // object (this method allocates a lot of dynamic memory in case a + // previously unknown clip/root combination is specified) + clipAction( clip, optionalRoot, blendMode ) { - } + const root = optionalRoot || this._root, + rootUuid = root.uuid; - geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); + let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - } + const clipUuid = clipObject !== null ? clipObject.uuid : clip; - // Add index. - if ( geometryType === draco.TRIANGULAR_MESH ) { + const actionsForClip = this._actionsByClip[ clipUuid ]; + let prototypeAction = null; - geometry.index = decodeIndex( draco, decoder, dracoGeometry ); + if ( blendMode === undefined ) { - } + if ( clipObject !== null ) { - draco.destroy( dracoGeometry ); + blendMode = clipObject.blendMode; - return geometry; + } else { - } + blendMode = NormalAnimationBlendMode; - function decodeIndex( draco, decoder, dracoGeometry ) { + } - const numFaces = dracoGeometry.num_faces(); - const numIndices = numFaces * 3; - const byteLength = numIndices * 4; + } - const ptr = draco._malloc( byteLength ); - decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); - const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); - draco._free( ptr ); + if ( actionsForClip !== undefined ) { - return { array: index, itemSize: 1 }; + const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - } + if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { + return existingAction; - const numComponents = attribute.num_components(); - const numPoints = dracoGeometry.num_points(); - const numValues = numPoints * numComponents; - const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; - const dataType = getDracoDataType( draco, attributeType ); + } - const ptr = draco._malloc( byteLength ); - decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); - const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); - draco._free( ptr ); + // we know the clip, so we don't have to parse all + // the bindings again but can just copy + prototypeAction = actionsForClip.knownActions[ 0 ]; - return { - name: attributeName, - array: array, - itemSize: numComponents - }; + // also, take the clip from the prototype action + if ( clipObject === null ) + clipObject = prototypeAction._clip; - } + } - function getDracoDataType( draco, attributeType ) { + // clip must be known when specified via string + if ( clipObject === null ) return null; - switch ( attributeType ) { + // allocate all resources required to run it + const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - case Float32Array: return draco.DT_FLOAT32; - case Int8Array: return draco.DT_INT8; - case Int16Array: return draco.DT_INT16; - case Int32Array: return draco.DT_INT32; - case Uint8Array: return draco.DT_UINT8; - case Uint16Array: return draco.DT_UINT16; - case Uint32Array: return draco.DT_UINT32; + this._bindAction( newAction, prototypeAction ); - } + // and make the action known to the memory manager + this._addInactiveAction( newAction, clipUuid, rootUuid ); + + return newAction; } -} + // get an existing action + existingAction( clip, optionalRoot ) { -class GLTFLoader extends Loader { + const root = optionalRoot || this._root, + rootUuid = root.uuid, - constructor( manager ) { + clipObject = typeof clip === 'string' ? + AnimationClip.findByName( root, clip ) : clip, - super( manager ); + clipUuid = clipObject ? clipObject.uuid : clip, - this.dracoLoader = null; - this.ktx2Loader = null; - this.meshoptDecoder = null; + actionsForClip = this._actionsByClip[ clipUuid ]; - this.pluginCallbacks = []; + if ( actionsForClip !== undefined ) { - this.register( function ( parser ) { + return actionsForClip.actionByRoot[ rootUuid ] || null; - return new GLTFMaterialsClearcoatExtension( parser ); + } - } ); + return null; - this.register( function ( parser ) { + } - return new GLTFTextureBasisUExtension( parser ); + // deactivates all previously scheduled actions + stopAllAction() { - } ); + const actions = this._actions, + nActions = this._nActiveActions; - this.register( function ( parser ) { + for ( let i = nActions - 1; i >= 0; -- i ) { - return new GLTFTextureWebPExtension( parser ); + actions[ i ].stop(); - } ); + } - this.register( function ( parser ) { + return this; - return new GLTFMaterialsTransmissionExtension( parser ); + } - } ); + // advance the time and update apply the animation + update( deltaTime ) { - this.register( function ( parser ) { + deltaTime *= this.timeScale; - return new GLTFLightsExtension( parser ); + const actions = this._actions, + nActions = this._nActiveActions, - } ); + time = this.time += deltaTime, + timeDirection = Math.sign( deltaTime ), - this.register( function ( parser ) { + accuIndex = this._accuIndex ^= 1; - return new GLTFMeshoptCompression( parser ); + // run active actions - } ); + for ( let i = 0; i !== nActions; ++ i ) { - } + const action = actions[ i ]; - load( url, onLoad, onProgress, onError ) { + action._update( time, deltaTime, timeDirection, accuIndex ); - const scope = this; + } - let resourcePath; + // update scene graph - if ( this.resourcePath !== '' ) { + const bindings = this._bindings, + nBindings = this._nActiveBindings; - resourcePath = this.resourcePath; + for ( let i = 0; i !== nBindings; ++ i ) { - } else if ( this.path !== '' ) { + bindings[ i ].apply( accuIndex ); - resourcePath = this.path; + } - } else { + return this; - resourcePath = LoaderUtils.extractUrlBase( url ); + } - } + // Allows you to seek to a specific time in an animation. + setTime( timeInSeconds ) { - // Tells the LoadingManager to track an extra item, which resolves after - // the model is fully loaded. This means the count of items loaded will - // be incorrect, but ensures manager.onLoad() does not fire early. - this.manager.itemStart( url ); + this.time = 0; // Zero out time attribute for AnimationMixer object; + for ( let i = 0; i < this._actions.length; i ++ ) { - const _onError = function ( e ) { + this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - if ( onError ) { + } - onError( e ); + return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - } else { + } - console.error( e ); + // return this mixer's root target object + getRoot() { - } + return this._root; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - }; + // free all resources specific to a particular clip + uncacheClip( clip ) { - const loader = new FileLoader( this.manager ); + const actions = this._actions, + clipUuid = clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ]; - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); - loader.setRequestHeader( this.requestHeader ); - loader.setWithCredentials( this.withCredentials ); + if ( actionsForClip !== undefined ) { - loader.load( url, function ( data ) { + // note: just calling _removeInactiveAction would mess up the + // iteration state and also require updating the state we can + // just throw away - try { + const actionsToRemove = actionsForClip.knownActions; - scope.parse( data, resourcePath, function ( gltf ) { + for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - onLoad( gltf ); + const action = actionsToRemove[ i ]; - scope.manager.itemEnd( url ); + this._deactivateAction( action ); - }, _onError ); + const cacheIndex = action._cacheIndex, + lastInactiveAction = actions[ actions.length - 1 ]; - } catch ( e ) { + action._cacheIndex = null; + action._byClipCacheIndex = null; - _onError( e ); + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); + + this._removeInactiveBindingsForAction( action ); } - }, onProgress, _onError ); + delete actionsByClip[ clipUuid ]; + + } } - setDRACOLoader( dracoLoader ) { + // free all resources specific to a particular root target object + uncacheRoot( root ) { - this.dracoLoader = dracoLoader; - return this; + const rootUuid = root.uuid, + actionsByClip = this._actionsByClip; - } + for ( const clipUuid in actionsByClip ) { - setDDSLoader() { + const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, + action = actionByRoot[ rootUuid ]; - throw new Error( + if ( action !== undefined ) { - 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' + this._deactivateAction( action ); + this._removeInactiveAction( action ); - ); + } - } + } - setKTX2Loader( ktx2Loader ) { + const bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ]; - this.ktx2Loader = ktx2Loader; - return this; + if ( bindingByName !== undefined ) { - } + for ( const trackName in bindingByName ) { - setMeshoptDecoder( meshoptDecoder ) { + const binding = bindingByName[ trackName ]; + binding.restoreOriginalState(); + this._removeInactiveBinding( binding ); - this.meshoptDecoder = meshoptDecoder; - return this; + } + + } } - register( callback ) { + // remove a targeted clip from the cache + uncacheAction( clip, optionalRoot ) { - if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { + const action = this.existingAction( clip, optionalRoot ); - this.pluginCallbacks.push( callback ); + if ( action !== null ) { - } + this._deactivateAction( action ); + this._removeInactiveAction( action ); - return this; + } } - unregister( callback ) { +} - if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { +AnimationMixer.prototype._controlInterpolantsResultBuffer = new Float32Array( 1 ); - this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); +class InstancedInterleavedBuffer extends InterleavedBuffer { - } + constructor( array, stride, meshPerAttribute = 1 ) { - return this; + super( array, stride ); + + this.meshPerAttribute = meshPerAttribute; } - parse( data, path, onLoad, onError ) { + copy( source ) { - let content; - const extensions = {}; - const plugins = {}; + super.copy( source ); - if ( typeof data === 'string' ) { + this.meshPerAttribute = source.meshPerAttribute; - content = data; + return this; - } else { + } - const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); + clone( data ) { - if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { + const ib = super.clone( data ); - try { + ib.meshPerAttribute = this.meshPerAttribute; - extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); + return ib; - } catch ( error ) { + } - if ( onError ) onError( error ); - return; + toJSON( data ) { - } + const json = super.toJSON( data ); - content = extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content; + json.isInstancedInterleavedBuffer = true; + json.meshPerAttribute = this.meshPerAttribute; - } else { + return json; - content = LoaderUtils.decodeText( new Uint8Array( data ) ); + } - } +} - } +InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; - const json = JSON.parse( content ); +class Raycaster { - if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { + constructor( origin, direction, near = 0, far = Infinity ) { - if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); - return; + this.ray = new Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) - } + this.near = near; + this.far = far; + this.camera = null; + this.layers = new Layers(); - const parser = new GLTFParser$1( json, { + this.params = { + Mesh: {}, + Line: { threshold: 1 }, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; - path: path || this.resourcePath || '', - crossOrigin: this.crossOrigin, - requestHeader: this.requestHeader, - manager: this.manager, - ktx2Loader: this.ktx2Loader, - meshoptDecoder: this.meshoptDecoder + } - } ); + set( origin, direction ) { - parser.fileLoader.setRequestHeader( this.requestHeader ); + // direction is assumed to be normalized (for accurate distance calculations) - for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { + this.ray.set( origin, direction ); - const plugin = this.pluginCallbacks[ i ]( parser ); - plugins[ plugin.name ] = plugin; + } - // Workaround to avoid determining as unknown extension - // in addUnknownExtensionsToUserData(). - // Remove this workaround if we move all the existing - // extension handlers to plugin system - extensions[ plugin.name ] = true; + setFromCamera( coords, camera ) { - } + if ( camera.isPerspectiveCamera ) { - if ( json.extensionsUsed ) { + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + this.camera = camera; - for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { + } else if ( camera.isOrthographicCamera ) { - const extensionName = json.extensionsUsed[ i ]; - const extensionsRequired = json.extensionsRequired || []; + this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + this.camera = camera; - switch ( extensionName ) { + } else { - case EXTENSIONS$1.KHR_MATERIALS_UNLIT: - extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); - break; + console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); - case EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: - extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); - break; + } - case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: - extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); - break; + } - case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: - extensions[ extensionName ] = new GLTFTextureTransformExtension(); - break; + intersectObject( object, recursive = true, intersects = [] ) { - case EXTENSIONS$1.KHR_MESH_QUANTIZATION: - extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); - break; + intersectObject( object, this, intersects, recursive ); - default: + intersects.sort( ascSort ); - if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { + return intersects; - console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); + } - } + intersectObjects( objects, recursive = true, intersects = [] ) { - } + for ( let i = 0, l = objects.length; i < l; i ++ ) { - } + intersectObject( objects[ i ], this, intersects, recursive ); } - parser.setExtensions( extensions ); - parser.setPlugins( plugins ); - parser.parse( onLoad, onError ); + intersects.sort( ascSort ); + + return intersects; } } -/* GLTFREGISTRY */ - -function GLTFRegistry$1() { +function ascSort( a, b ) { - let objects = {}; + return a.distance - b.distance; - return { +} - get: function ( key ) { +function intersectObject( object, raycaster, intersects, recursive ) { - return objects[ key ]; + if ( object.layers.test( raycaster.layers ) ) { - }, + object.raycast( raycaster, intersects ); - add: function ( key, object ) { + } - objects[ key ] = object; + if ( recursive === true ) { - }, + const children = object.children; - remove: function ( key ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - delete objects[ key ]; + intersectObject( children[ i ], raycaster, intersects, true ); - }, + } - removeAll: function () { + } - objects = {}; +} - } +/** + * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system + * + * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. + * The azimuthal angle (theta) is measured from the positive z-axis. + */ - }; +class Spherical { -} + constructor( radius = 1, phi = 0, theta = 0 ) { -/*********************************/ -/********** EXTENSIONS ***********/ -/*********************************/ + this.radius = radius; + this.phi = phi; // polar angle + this.theta = theta; // azimuthal angle -const EXTENSIONS$1 = { - KHR_BINARY_GLTF: 'KHR_binary_glTF', - KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', - KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', - KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', - KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', - KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', - KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', - KHR_TEXTURE_BASISU: 'KHR_texture_basisu', - KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', - KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', - EXT_TEXTURE_WEBP: 'EXT_texture_webp', - EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' -}; + return this; -/** - * Punctual Lights Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - */ -class GLTFLightsExtension { + } - constructor( parser ) { + set( radius, phi, theta ) { - this.parser = parser; - this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; + this.radius = radius; + this.phi = phi; + this.theta = theta; - // Object3D instance caches - this.cache = { refs: {}, uses: {} }; + return this; } - _markDefs() { + copy( other ) { - const parser = this.parser; - const nodeDefs = this.parser.json.nodes || []; + this.radius = other.radius; + this.phi = other.phi; + this.theta = other.theta; - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + return this; - const nodeDef = nodeDefs[ nodeIndex ]; + } - if ( nodeDef.extensions - && nodeDef.extensions[ this.name ] - && nodeDef.extensions[ this.name ].light !== undefined ) { + // restrict phi to be between EPS and PI-EPS + makeSafe() { - parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); + const EPS = 0.000001; + this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); - } + return this; - } + } + + setFromVector3( v ) { + + return this.setFromCartesianCoords( v.x, v.y, v.z ); } - _loadLight( lightIndex ) { + setFromCartesianCoords( x, y, z ) { - const parser = this.parser; - const cacheKey = 'light:' + lightIndex; - let dependency = parser.cache.get( cacheKey ); + this.radius = Math.sqrt( x * x + y * y + z * z ); - if ( dependency ) return dependency; + if ( this.radius === 0 ) { - const json = parser.json; - const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; - const lightDefs = extensions.lights || []; - const lightDef = lightDefs[ lightIndex ]; - let lightNode; + this.theta = 0; + this.phi = 0; - const color = new Color( 0xffffff ); + } else { - if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); + this.theta = Math.atan2( x, z ); + this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); - const range = lightDef.range !== undefined ? lightDef.range : 0; + } - switch ( lightDef.type ) { + return this; - case 'directional': - lightNode = new DirectionalLight( color ); - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; + } - case 'point': - lightNode = new PointLight( color ); - lightNode.distance = range; - break; - - case 'spot': - lightNode = new SpotLight( color ); - lightNode.distance = range; - // Handle spotlight properties. - lightDef.spot = lightDef.spot || {}; - lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; - lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; - lightNode.angle = lightDef.spot.outerConeAngle; - lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; - lightNode.target.position.set( 0, 0, - 1 ); - lightNode.add( lightNode.target ); - break; - - default: - throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); + clone() { - } + return new this.constructor().copy( this ); - // Some lights (e.g. spot) default to a position other than the origin. Reset the position - // here, because node-level parsing will only override position if explicitly specified. - lightNode.position.set( 0, 0, 0 ); + } - lightNode.decay = 2; +} - if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; +const _vector$2 = /*@__PURE__*/ new Vector3(); +const _boneMatrix = /*@__PURE__*/ new Matrix4(); +const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); - lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - dependency = Promise.resolve( lightNode ); +class SkeletonHelper extends LineSegments { - parser.cache.add( cacheKey, dependency ); + constructor( object ) { - return dependency; + const bones = getBoneList( object ); - } + const geometry = new BufferGeometry(); - createNodeAttachment( nodeIndex ) { + const vertices = []; + const colors = []; - const self = this; - const parser = this.parser; - const json = parser.json; - const nodeDef = json.nodes[ nodeIndex ]; - const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; - const lightIndex = lightDef.light; + const color1 = new Color( 0, 0, 1 ); + const color2 = new Color( 0, 1, 0 ); - if ( lightIndex === undefined ) return null; + for ( let i = 0; i < bones.length; i ++ ) { - return this._loadLight( lightIndex ).then( function ( light ) { + const bone = bones[ i ]; - return parser._getNodeRef( self.cache, lightIndex, light ); + if ( bone.parent && bone.parent.isBone ) { - } ); + vertices.push( 0, 0, 0 ); + vertices.push( 0, 0, 0 ); + colors.push( color1.r, color1.g, color1.b ); + colors.push( color2.r, color2.g, color2.b ); - } + } -} + } -/** - * Unlit Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit - */ -class GLTFMaterialsUnlitExtension { + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - constructor() { + const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; + super( geometry, material ); - } + this.type = 'SkeletonHelper'; + this.isSkeletonHelper = true; - getMaterialType() { + this.root = object; + this.bones = bones; - return MeshBasicMaterial; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; } - extendParams( materialParams, materialDef, parser ) { - - const pending = []; + updateMatrixWorld( force ) { - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; + const bones = this.bones; - const metallicRoughness = materialDef.pbrMetallicRoughness; + const geometry = this.geometry; + const position = geometry.getAttribute( 'position' ); - if ( metallicRoughness ) { + _matrixWorldInv.copy( this.root.matrixWorld ).invert(); - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + for ( let i = 0, j = 0; i < bones.length; i ++ ) { - const array = metallicRoughness.baseColorFactor; + const bone = bones[ i ]; - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; + if ( bone.parent && bone.parent.isBone ) { - } + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); - if ( metallicRoughness.baseColorTexture !== undefined ) { + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); + j += 2; } } - return Promise.all( pending ); + geometry.getAttribute( 'position' ).needsUpdate = true; + + super.updateMatrixWorld( force ); } } -/** - * Clearcoat Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat - */ -class GLTFMaterialsClearcoatExtension { - constructor( parser ) { +function getBoneList( object ) { - this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; + const boneList = []; - } + if ( object.isBone === true ) { - getMaterialType( materialIndex ) { + boneList.push( object ); - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + } - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; + for ( let i = 0; i < object.children.length; i ++ ) { - return MeshPhysicalMaterial; + boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); } - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + return boneList; - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { +} - return Promise.resolve(); +class GridHelper extends LineSegments { - } + constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { - const pending = []; + color1 = new Color( color1 ); + color2 = new Color( color2 ); - const extension = materialDef.extensions[ this.name ]; + const center = divisions / 2; + const step = size / divisions; + const halfSize = size / 2; - if ( extension.clearcoatFactor !== undefined ) { + const vertices = [], colors = []; - materialParams.clearcoat = extension.clearcoatFactor; + for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - } + vertices.push( - halfSize, 0, k, halfSize, 0, k ); + vertices.push( k, 0, - halfSize, k, 0, halfSize ); - if ( extension.clearcoatTexture !== undefined ) { + const color = i === center ? color1 : color2; - pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; } - if ( extension.clearcoatRoughnessFactor !== undefined ) { + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - } + super( geometry, material ); - if ( extension.clearcoatRoughnessTexture !== undefined ) { + this.type = 'GridHelper'; - pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); + } - } +} - if ( extension.clearcoatNormalTexture !== undefined ) { +const _baseTable = new Uint32Array( 512 ); +const _shiftTable = new Uint32Array( 512 ); - pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); +for ( let i = 0; i < 256; ++ i ) { - if ( extension.clearcoatNormalTexture.scale !== undefined ) { + const e = i - 127; - const scale = extension.clearcoatNormalTexture.scale; + // very small number (0, -0) - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.clearcoatNormalScale = new Vector2( scale, - scale ); + if ( e < - 27 ) { - } + _baseTable[ i ] = 0x0000; + _baseTable[ i | 0x100 ] = 0x8000; + _shiftTable[ i ] = 24; + _shiftTable[ i | 0x100 ] = 24; - } + // small number (denorm) - return Promise.all( pending ); + } else if ( e < - 14 ) { - } + _baseTable[ i ] = 0x0400 >> ( - e - 14 ); + _baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; + _shiftTable[ i ] = - e - 1; + _shiftTable[ i | 0x100 ] = - e - 1; -} + // normal number -/** - * Transmission Materials Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission - * Draft: https://github.com/KhronosGroup/glTF/pull/1698 - */ -class GLTFMaterialsTransmissionExtension { + } else if ( e <= 15 ) { - constructor( parser ) { + _baseTable[ i ] = ( e + 15 ) << 10; + _baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; + _shiftTable[ i ] = 13; + _shiftTable[ i | 0x100 ] = 13; - this.parser = parser; - this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; + // large number (Infinity, -Infinity) - } + } else if ( e < 128 ) { - getMaterialType( materialIndex ) { + _baseTable[ i ] = 0x7c00; + _baseTable[ i | 0x100 ] = 0xfc00; + _shiftTable[ i ] = 24; + _shiftTable[ i | 0x100 ] = 24; - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; + // stay (NaN, Infinity, -Infinity) - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; + } else { - return MeshPhysicalMaterial; + _baseTable[ i ] = 0x7c00; + _baseTable[ i | 0x100 ] = 0xfc00; + _shiftTable[ i ] = 13; + _shiftTable[ i | 0x100 ] = 13; } - extendMaterialParams( materialIndex, materialParams ) { - - const parser = this.parser; - const materialDef = parser.json.materials[ materialIndex ]; - - if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { +} - return Promise.resolve(); +// float16 to float32 helpers - } +const _mantissaTable = new Uint32Array( 2048 ); +const _exponentTable = new Uint32Array( 64 ); +const _offsetTable = new Uint32Array( 64 ); - const pending = []; +for ( let i = 1; i < 1024; ++ i ) { - const extension = materialDef.extensions[ this.name ]; + let m = i << 13; // zero pad mantissa bits + let e = 0; // zero exponent - if ( extension.transmissionFactor !== undefined ) { + // normalized + while ( ( m & 0x00800000 ) === 0 ) { - materialParams.transmission = extension.transmissionFactor; + m <<= 1; + e -= 0x00800000; // decrement exponent - } + } - if ( extension.transmissionTexture !== undefined ) { + m &= ~ 0x00800000; // clear leading 1 bit + e += 0x38800000; // adjust bias - pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); + _mantissaTable[ i ] = m | e; - } +} - return Promise.all( pending ); +for ( let i = 1024; i < 2048; ++ i ) { - } + _mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); } -/** - * BasisU Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu - */ -class GLTFTextureBasisUExtension { +for ( let i = 1; i < 31; ++ i ) { - constructor( parser ) { + _exponentTable[ i ] = i << 23; - this.parser = parser; - this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; +} - } +_exponentTable[ 31 ] = 0x47800000; +_exponentTable[ 32 ] = 0x80000000; +for ( let i = 33; i < 63; ++ i ) { - loadTexture( textureIndex ) { + _exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); - const parser = this.parser; - const json = parser.json; +} - const textureDef = json.textures[ textureIndex ]; +_exponentTable[ 63 ] = 0xc7800000; - if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { +for ( let i = 1; i < 64; ++ i ) { - return null; + if ( i !== 32 ) { - } + _offsetTable[ i ] = 1024; - const extension = textureDef.extensions[ this.name ]; - const source = json.images[ extension.source ]; - const loader = parser.options.ktx2Loader; + } - if ( ! loader ) { +} - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { +// - throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); +Curve.create = function ( construct, getPoint ) { - } else { + console.log( 'THREE.Curve.create() has been deprecated' ); - // Assumes that the extension is optional and that a fallback texture is present - return null; + construct.prototype = Object.create( Curve.prototype ); + construct.prototype.constructor = construct; + construct.prototype.getPoint = getPoint; - } + return construct; - } +}; - return parser.loadTextureImage( textureIndex, source, loader ); +// - } +Path.prototype.fromPoints = function ( points ) { -} + console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); + return this.setFromPoints( points ); -/** - * WebP Texture Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp - */ -class GLTFTextureWebPExtension { +}; - constructor( parser ) { +GridHelper.prototype.setColors = function () { - this.parser = parser; - this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; - this.isSupported = null; + console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - } +}; - loadTexture( textureIndex ) { +SkeletonHelper.prototype.update = function () { - const name = this.name; - const parser = this.parser; - const json = parser.json; + console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); - const textureDef = json.textures[ textureIndex ]; +}; - if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { +// - return null; +Loader.prototype.extractUrlBase = function ( url ) { - } + console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); + return LoaderUtils.extractUrlBase( url ); - const extension = textureDef.extensions[ name ]; - const source = json.images[ extension.source ]; +}; - let loader = parser.textureLoader; - if ( source.uri ) { +Loader.Handlers = { - const handler = parser.options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; + add: function ( /* regex, loader */ ) { - } + console.error( 'THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.' ); - return this.detectSupport().then( function ( isSupported ) { + }, - if ( isSupported ) return parser.loadTextureImage( textureIndex, source, loader ); + get: function ( /* file */ ) { - if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { + console.error( 'THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.' ); - throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); + } - } +}; - // Fall back to PNG or JPEG. - return parser.loadTexture( textureIndex ); +// - } ); +Box3.prototype.center = function ( optionalTarget ) { - } + console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); + return this.getCenter( optionalTarget ); - detectSupport() { +}; - if ( ! this.isSupported ) { +Box3.prototype.empty = function () { - this.isSupported = new Promise( function ( resolve ) { + console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - const image = new Image(); +}; - // Lossy test image. Support for lossy images doesn't guarantee support for all - // WebP images, unfortunately. - image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; +Box3.prototype.isIntersectionBox = function ( box ) { - image.onload = image.onerror = function () { + console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); - resolve( image.height === 1 ); +}; - }; +Box3.prototype.isIntersectionSphere = function ( sphere ) { - } ); + console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); - } +}; - return this.isSupported; +Box3.prototype.size = function ( optionalTarget ) { - } + console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); + return this.getSize( optionalTarget ); -} +}; -/** - * meshopt BufferView Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression - */ -class GLTFMeshoptCompression { +// - constructor( parser ) { +Euler.prototype.toVector3 = function () { - this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; - this.parser = parser; + console.error( 'THREE.Euler: .toVector3() has been removed. Use Vector3.setFromEuler() instead' ); - } +}; - loadBufferView( index ) { - const json = this.parser.json; - const bufferView = json.bufferViews[ index ]; +// - if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { +Sphere.prototype.empty = function () { - const extensionDef = bufferView.extensions[ this.name ]; + console.warn( 'THREE.Sphere: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); - const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); - const decoder = this.parser.options.meshoptDecoder; +}; - if ( ! decoder || ! decoder.supported ) { +// - if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { +Frustum.prototype.setFromMatrix = function ( m ) { - throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); + console.warn( 'THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix().' ); + return this.setFromProjectionMatrix( m ); - } else { +}; - // Assumes that the extension is optional and that fallback buffer data is present - return null; +// - } +Matrix3.prototype.flattenToArrayOffset = function ( array, offset ) { - } + console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { +}; - const byteOffset = extensionDef.byteOffset || 0; - const byteLength = extensionDef.byteLength || 0; +Matrix3.prototype.multiplyVector3 = function ( vector ) { - const count = extensionDef.count; - const stride = extensionDef.byteStride; + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); - const result = new ArrayBuffer( count * stride ); - const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); +}; - decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); - return result; +Matrix3.prototype.multiplyVector3Array = function ( /* a */ ) { - } ); + console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); - } else { +}; - return null; +Matrix3.prototype.applyToBufferAttribute = function ( attribute ) { - } + console.warn( 'THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead.' ); + return attribute.applyMatrix3( this ); - } +}; -} +Matrix3.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { -/* BINARY EXTENSION */ -const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; -const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; -const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; + console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); -class GLTFBinaryExtension$1 { +}; - constructor( data ) { +Matrix3.prototype.getInverse = function ( matrix ) { - this.name = EXTENSIONS$1.KHR_BINARY_GLTF; - this.content = null; - this.body = null; + console.warn( 'THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); +}; - this.header = { - magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), - version: headerView.getUint32( 4, true ), - length: headerView.getUint32( 8, true ) - }; +// - if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { +Matrix4.prototype.extractPosition = function ( m ) { - throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); - } else if ( this.header.version < 2.0 ) { +}; - throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); +Matrix4.prototype.flattenToArrayOffset = function ( array, offset ) { - } + console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' ); + return this.toArray( array, offset ); - const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; - const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); - let chunkIndex = 0; +}; - while ( chunkIndex < chunkContentsLength ) { +Matrix4.prototype.getPosition = function () { - const chunkLength = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + return new Vector3().setFromMatrixColumn( this, 3 ); - const chunkType = chunkView.getUint32( chunkIndex, true ); - chunkIndex += 4; +}; - if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { +Matrix4.prototype.setRotationFromQuaternion = function ( q ) { - const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); - this.content = LoaderUtils.decodeText( contentArray ); + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + return this.makeRotationFromQuaternion( q ); - } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { +}; - const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; - this.body = data.slice( byteOffset, byteOffset + chunkLength ); +Matrix4.prototype.multiplyToArray = function () { - } + console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); - // Clients must ignore chunks with unknown types. +}; - chunkIndex += chunkLength; +Matrix4.prototype.multiplyVector3 = function ( vector ) { - } + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - if ( this.content === null ) { +}; - throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); +Matrix4.prototype.multiplyVector4 = function ( vector ) { - } + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - } +}; -} +Matrix4.prototype.multiplyVector3Array = function ( /* a */ ) { -/** - * DRACO Mesh Compression Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression - */ -class GLTFDracoMeshCompressionExtension { + console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); - constructor( json, dracoLoader ) { +}; - if ( ! dracoLoader ) { +Matrix4.prototype.rotateAxis = function ( v ) { - throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); - - } - - this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; - this.json = json; - this.dracoLoader = dracoLoader; - this.dracoLoader.preload(); + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + v.transformDirection( this ); - } +}; - decodePrimitive( primitive, parser ) { +Matrix4.prototype.crossVector = function ( vector ) { - const json = this.json; - const dracoLoader = this.dracoLoader; - const bufferViewIndex = primitive.extensions[ this.name ].bufferView; - const gltfAttributeMap = primitive.extensions[ this.name ].attributes; - const threeAttributeMap = {}; - const attributeNormalizedMap = {}; - const attributeTypeMap = {}; + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); - for ( const attributeName in gltfAttributeMap ) { +}; - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); +Matrix4.prototype.translate = function () { - threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; + console.error( 'THREE.Matrix4: .translate() has been removed.' ); - } +}; - for ( const attributeName in primitive.attributes ) { +Matrix4.prototype.rotateX = function () { - const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); - if ( gltfAttributeMap[ attributeName ] !== undefined ) { +}; - const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; - const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; +Matrix4.prototype.rotateY = function () { - attributeTypeMap[ threeAttributeName ] = componentType; - attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); - } +}; - } +Matrix4.prototype.rotateZ = function () { - return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); - return new Promise( function ( resolve ) { +}; - dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { +Matrix4.prototype.rotateByAxis = function () { - for ( const attributeName in geometry.attributes ) { + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); - const attribute = geometry.attributes[ attributeName ]; - const normalized = attributeNormalizedMap[ attributeName ]; +}; - if ( normalized !== undefined ) attribute.normalized = normalized; +Matrix4.prototype.applyToBufferAttribute = function ( attribute ) { - } + console.warn( 'THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead.' ); + return attribute.applyMatrix4( this ); - resolve( geometry ); +}; - }, threeAttributeMap, attributeTypeMap ); +Matrix4.prototype.applyToVector3Array = function ( /* array, offset, length */ ) { - } ); + console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); - } ); +}; - } +Matrix4.prototype.makeFrustum = function ( left, right, bottom, top, near, far ) { -} + console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); + return this.makePerspective( left, right, top, bottom, near, far ); -/** - * Texture Transform Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform - */ -class GLTFTextureTransformExtension { +}; - constructor() { +Matrix4.prototype.getInverse = function ( matrix ) { - this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + console.warn( 'THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead.' ); + return this.copy( matrix ).invert(); - } +}; - extendTexture( texture, transform ) { +// - texture = texture.clone(); +Plane.prototype.isIntersectionLine = function ( line ) { - if ( transform.offset !== undefined ) { + console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); + return this.intersectsLine( line ); - texture.offset.fromArray( transform.offset ); +}; - } +// - if ( transform.rotation !== undefined ) { +Quaternion.prototype.multiplyVector3 = function ( vector ) { - texture.rotation = transform.rotation; + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); - } +}; - if ( transform.scale !== undefined ) { +Quaternion.prototype.inverse = function ( ) { - texture.repeat.fromArray( transform.scale ); + console.warn( 'THREE.Quaternion: .inverse() has been renamed to invert().' ); + return this.invert(); - } +}; - if ( transform.texCoord !== undefined ) { +// - console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); +Ray.prototype.isIntersectionBox = function ( box ) { - } + console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); - texture.needsUpdate = true; +}; - return texture; +Ray.prototype.isIntersectionPlane = function ( plane ) { - } + console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); + return this.intersectsPlane( plane ); -} +}; -/** - * Specular-Glossiness Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness - */ +Ray.prototype.isIntersectionSphere = function ( sphere ) { -/** - * A sub class of StandardMaterial with some of the functionality - * changed via the `onBeforeCompile` callback - * @pailhead - */ + console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); -class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { +}; - constructor( params ) { +// - super(); +Triangle.prototype.area = function () { - this.isGLTFSpecularGlossinessMaterial = true; + console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); + return this.getArea(); - //various chunks that need replacing - const specularMapParsFragmentChunk = [ - '#ifdef USE_SPECULARMAP', - ' uniform sampler2D specularMap;', - '#endif' - ].join( '\n' ); +}; - const glossinessMapParsFragmentChunk = [ - '#ifdef USE_GLOSSINESSMAP', - ' uniform sampler2D glossinessMap;', - '#endif' - ].join( '\n' ); +Triangle.prototype.barycoordFromPoint = function ( point, target ) { - const specularMapFragmentChunk = [ - 'vec3 specularFactor = specular;', - '#ifdef USE_SPECULARMAP', - ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', - ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' specularFactor *= texelSpecular.rgb;', - '#endif' - ].join( '\n' ); + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return this.getBarycoord( point, target ); - const glossinessMapFragmentChunk = [ - 'float glossinessFactor = glossiness;', - '#ifdef USE_GLOSSINESSMAP', - ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', - ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', - ' glossinessFactor *= texelGlossiness.a;', - '#endif' - ].join( '\n' ); +}; - const lightPhysicalFragmentChunk = [ - 'PhysicalMaterial material;', - 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', - 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', - 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', - 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', - 'material.specularRoughness += geometryRoughness;', - 'material.specularRoughness = min( material.specularRoughness, 1.0 );', - 'material.specularColor = specularFactor;', - ].join( '\n' ); +Triangle.prototype.midpoint = function ( target ) { - const uniforms = { - specular: { value: new Color().setHex( 0xffffff ) }, - glossiness: { value: 1 }, - specularMap: { value: null }, - glossinessMap: { value: null } - }; + console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); + return this.getMidpoint( target ); - this._extraUniforms = uniforms; +}; - this.onBeforeCompile = function ( shader ) { +Triangle.prototypenormal = function ( target ) { - for ( const uniformName in uniforms ) { + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return this.getNormal( target ); - shader.uniforms[ uniformName ] = uniforms[ uniformName ]; +}; - } +Triangle.prototype.plane = function ( target ) { - shader.fragmentShader = shader.fragmentShader - .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) - .replace( 'uniform float metalness;', 'uniform float glossiness;' ) - .replace( '#include ', specularMapParsFragmentChunk ) - .replace( '#include ', glossinessMapParsFragmentChunk ) - .replace( '#include ', specularMapFragmentChunk ) - .replace( '#include ', glossinessMapFragmentChunk ) - .replace( '#include ', lightPhysicalFragmentChunk ); + console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); + return this.getPlane( target ); - }; +}; - Object.defineProperties( this, { +Triangle.barycoordFromPoint = function ( point, a, b, c, target ) { - specular: { - get: function () { + console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); + return Triangle.getBarycoord( point, a, b, c, target ); - return uniforms.specular.value; +}; - }, - set: function ( v ) { +Triangle.normal = function ( a, b, c, target ) { - uniforms.specular.value = v; + console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); + return Triangle.getNormal( a, b, c, target ); - } - }, +}; - specularMap: { - get: function () { +// - return uniforms.specularMap.value; +Shape.prototype.extractAllPoints = function ( divisions ) { - }, - set: function ( v ) { + console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); + return this.extractPoints( divisions ); - uniforms.specularMap.value = v; +}; - if ( v ) { +Shape.prototype.extrude = function ( options ) { - this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps + console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); + return new ExtrudeGeometry( this, options ); - } else { +}; - delete this.defines.USE_SPECULARMAP; +Shape.prototype.makeGeometry = function ( options ) { - } + console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); + return new ShapeGeometry( this, options ); - } - }, +}; - glossiness: { - get: function () { +// - return uniforms.glossiness.value; +Vector2.prototype.fromAttribute = function ( attribute, index, offset ) { - }, - set: function ( v ) { + console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - uniforms.glossiness.value = v; +}; - } - }, +Vector2.prototype.distanceToManhattan = function ( v ) { - glossinessMap: { - get: function () { + console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - return uniforms.glossinessMap.value; +}; - }, - set: function ( v ) { +Vector2.prototype.lengthManhattan = function () { - uniforms.glossinessMap.value = v; + console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - if ( v ) { +}; - this.defines.USE_GLOSSINESSMAP = ''; - this.defines.USE_UV = ''; +// - } else { +Vector3.prototype.setEulerFromRotationMatrix = function () { - delete this.defines.USE_GLOSSINESSMAP; - delete this.defines.USE_UV; + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); - } +}; - } - } +Vector3.prototype.setEulerFromQuaternion = function () { - } ); + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; +}; - this.setValues( params ); +Vector3.prototype.getPositionFromMatrix = function ( m ) { - } + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + return this.setFromMatrixPosition( m ); - copy( source ) { +}; - super.copy( source ); +Vector3.prototype.getScaleFromMatrix = function ( m ) { - this.specularMap = source.specularMap; - this.specular.copy( source.specular ); - this.glossinessMap = source.glossinessMap; - this.glossiness = source.glossiness; - delete this.metalness; - delete this.roughness; - delete this.metalnessMap; - delete this.roughnessMap; - return this; + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + return this.setFromMatrixScale( m ); - } +}; -} +Vector3.prototype.getColumnFromMatrix = function ( index, matrix ) { + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + return this.setFromMatrixColumn( matrix, index ); -class GLTFMaterialsPbrSpecularGlossinessExtension { +}; - constructor() { +Vector3.prototype.applyProjection = function ( m ) { - this.name = EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; + console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); + return this.applyMatrix4( m ); - this.specularGlossinessParams = [ - 'color', - 'map', - 'lightMap', - 'lightMapIntensity', - 'aoMap', - 'aoMapIntensity', - 'emissive', - 'emissiveIntensity', - 'emissiveMap', - 'bumpMap', - 'bumpScale', - 'normalMap', - 'normalMapType', - 'displacementMap', - 'displacementScale', - 'displacementBias', - 'specularMap', - 'specular', - 'glossinessMap', - 'glossiness', - 'alphaMap', - 'envMap', - 'envMapIntensity', - 'refractionRatio', - ]; +}; - } +Vector3.prototype.fromAttribute = function ( attribute, index, offset ) { - getMaterialType() { + console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - return GLTFMeshStandardSGMaterial; +}; - } +Vector3.prototype.distanceToManhattan = function ( v ) { - extendParams( materialParams, materialDef, parser ) { + console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); + return this.manhattanDistanceTo( v ); - const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; +}; - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; +Vector3.prototype.lengthManhattan = function () { - const pending = []; + console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { +}; - const array = pbrSpecularGlossiness.diffuseFactor; +// - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; +Vector4.prototype.fromAttribute = function ( attribute, index, offset ) { - } + console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); + return this.fromBufferAttribute( attribute, index, offset ); - if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { +}; - pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) ); +Vector4.prototype.lengthManhattan = function () { - } + console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); + return this.manhattanLength(); - materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); - materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; - materialParams.specular = new Color( 1.0, 1.0, 1.0 ); +}; - if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { +// - materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); +Object3D.prototype.getChildByName = function ( name ) { - } + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); - if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { +}; - const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; - pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); - pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) ); +Object3D.prototype.renderDepth = function () { - } + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); - return Promise.all( pending ); +}; - } +Object3D.prototype.translate = function ( distance, axis ) { - createMaterial( materialParams ) { + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); - const material = new GLTFMeshStandardSGMaterial( materialParams ); - material.fog = true; +}; - material.color = materialParams.color; +Object3D.prototype.getWorldRotation = function () { - material.map = materialParams.map === undefined ? null : materialParams.map; + console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); - material.lightMap = null; - material.lightMapIntensity = 1.0; +}; - material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; - material.aoMapIntensity = 1.0; +Object3D.prototype.applyMatrix = function ( matrix ) { - material.emissive = materialParams.emissive; - material.emissiveIntensity = 1.0; - material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; + console.warn( 'THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); - material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; - material.bumpScale = 1; +}; - material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; - material.normalMapType = TangentSpaceNormalMap; +Object.defineProperties( Object3D.prototype, { - if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; + eulerOrder: { + get: function () { - material.displacementMap = null; - material.displacementScale = 1; - material.displacementBias = 0; + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + return this.rotation.order; - material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; - material.specular = materialParams.specular; + }, + set: function ( value ) { - material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; - material.glossiness = materialParams.glossiness; + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + this.rotation.order = value; - material.alphaMap = null; + } + }, + useQuaternion: { + get: function () { - material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; - material.envMapIntensity = 1.0; + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); - material.refractionRatio = 0.98; + }, + set: function () { - return material; + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + } } -} - -/** - * Mesh Quantization Extension - * - * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization - */ -class GLTFMeshQuantizationExtension { +} ); - constructor() { +Mesh.prototype.setDrawMode = function () { - this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + console.error( 'THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); - } +}; -} +Object.defineProperties( Mesh.prototype, { -/*********************************/ -/********** INTERPOLATION ********/ -/*********************************/ + drawMode: { + get: function () { -// Spline Interpolation -// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation -class GLTFCubicSplineInterpolant extends Interpolant { + console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode.' ); + return TrianglesDrawMode; - constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + }, + set: function () { - super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + console.error( 'THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.' ); + } } - copySampleValue_( index ) { +} ); - // Copies a sample value to the result buffer. See description of glTF - // CUBICSPLINE values layout in interpolate_() function below. +SkinnedMesh.prototype.initBones = function () { - const result = this.resultBuffer, - values = this.sampleValues, - valueSize = this.valueSize, - offset = index * valueSize * 3 + valueSize; + console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); - for ( let i = 0; i !== valueSize; i ++ ) { +}; - result[ i ] = values[ offset + i ]; +// - } +PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { - return result; + console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' + + 'Use .setFocalLength and .filmGauge for a photographic setup.' ); - } + if ( filmGauge !== undefined ) this.filmGauge = filmGauge; + this.setFocalLength( focalLength ); -} +}; -GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; +// -GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; +Object.defineProperties( Light.prototype, { + onlyShadow: { + set: function () { -GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); - const result = this.resultBuffer; - const values = this.sampleValues; - const stride = this.valueSize; + } + }, + shadowCameraFov: { + set: function ( value ) { - const stride2 = stride * 2; - const stride3 = stride * 3; + console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); + this.shadow.camera.fov = value; - const td = t1 - t0; + } + }, + shadowCameraLeft: { + set: function ( value ) { - const p = ( t - t0 ) / td; - const pp = p * p; - const ppp = pp * p; + console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); + this.shadow.camera.left = value; - const offset1 = i1 * stride3; - const offset0 = offset1 - stride3; + } + }, + shadowCameraRight: { + set: function ( value ) { - const s2 = - 2 * ppp + 3 * pp; - const s3 = ppp - pp; - const s0 = 1 - s2; - const s1 = s3 - pp + p; + console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); + this.shadow.camera.right = value; - // Layout of keyframe output values for CUBICSPLINE animations: - // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] - for ( let i = 0; i !== stride; i ++ ) { + } + }, + shadowCameraTop: { + set: function ( value ) { - const p0 = values[ offset0 + i + stride ]; // splineVertex_k - const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) - const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 - const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) + console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); + this.shadow.camera.top = value; - result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; + } + }, + shadowCameraBottom: { + set: function ( value ) { - } + console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); + this.shadow.camera.bottom = value; - return result; + } + }, + shadowCameraNear: { + set: function ( value ) { -}; + console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); + this.shadow.camera.near = value; -/*********************************/ -/********** INTERNALS ************/ -/*********************************/ + } + }, + shadowCameraFar: { + set: function ( value ) { -/* CONSTANTS */ + console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); + this.shadow.camera.far = value; -const WEBGL_CONSTANTS$1 = { - FLOAT: 5126, - //FLOAT_MAT2: 35674, - FLOAT_MAT3: 35675, - FLOAT_MAT4: 35676, - FLOAT_VEC2: 35664, - FLOAT_VEC3: 35665, - FLOAT_VEC4: 35666, - LINEAR: 9729, - REPEAT: 10497, - SAMPLER_2D: 35678, - POINTS: 0, - LINES: 1, - LINE_LOOP: 2, - LINE_STRIP: 3, - TRIANGLES: 4, - TRIANGLE_STRIP: 5, - TRIANGLE_FAN: 6, - UNSIGNED_BYTE: 5121, - UNSIGNED_SHORT: 5123 -}; + } + }, + shadowCameraVisible: { + set: function () { -const WEBGL_COMPONENT_TYPES$1 = { - 5120: Int8Array, - 5121: Uint8Array, - 5122: Int16Array, - 5123: Uint16Array, - 5125: Uint32Array, - 5126: Float32Array -}; + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); -const WEBGL_FILTERS$1 = { - 9728: NearestFilter, - 9729: LinearFilter, - 9984: NearestMipmapNearestFilter, - 9985: LinearMipmapNearestFilter, - 9986: NearestMipmapLinearFilter, - 9987: LinearMipmapLinearFilter -}; + } + }, + shadowBias: { + set: function ( value ) { -const WEBGL_WRAPPINGS$1 = { - 33071: ClampToEdgeWrapping, - 33648: MirroredRepeatWrapping, - 10497: RepeatWrapping -}; + console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); + this.shadow.bias = value; -const WEBGL_TYPE_SIZES$1 = { - 'SCALAR': 1, - 'VEC2': 2, - 'VEC3': 3, - 'VEC4': 4, - 'MAT2': 4, - 'MAT3': 9, - 'MAT4': 16 -}; + } + }, + shadowDarkness: { + set: function () { -const ATTRIBUTES = { - POSITION: 'position', - NORMAL: 'normal', - TANGENT: 'tangent', - TEXCOORD_0: 'uv', - TEXCOORD_1: 'uv2', - COLOR_0: 'color', - WEIGHTS_0: 'skinWeight', - JOINTS_0: 'skinIndex', -}; + console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); -const PATH_PROPERTIES$1 = { - scale: 'scale', - translation: 'position', - rotation: 'quaternion', - weights: 'morphTargetInfluences' -}; + } + }, + shadowMapWidth: { + set: function ( value ) { -const INTERPOLATION$1 = { - CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each - // keyframe track will be initialized with a default interpolation type, then modified. - LINEAR: InterpolateLinear, - STEP: InterpolateDiscrete -}; + console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); + this.shadow.mapSize.width = value; -const ALPHA_MODES = { - OPAQUE: 'OPAQUE', - MASK: 'MASK', - BLEND: 'BLEND' -}; + } + }, + shadowMapHeight: { + set: function ( value ) { -/* UTILITY FUNCTIONS */ + console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); + this.shadow.mapSize.height = value; -function resolveURL$1( url, path ) { + } + } +} ); - // Invalid URL - if ( typeof url !== 'string' || url === '' ) return ''; +// - // Host Relative URL - if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { +Object.defineProperties( BufferAttribute.prototype, { - path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); + length: { + get: function () { - } + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); + return this.array.length; - // Absolute URL http://,https://,// - if ( /^(https?:)?\/\//i.test( url ) ) return url; + } + }, + dynamic: { + get: function () { - // Data URI - if ( /^data:.*,.*$/i.test( url ) ) return url; + console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); + return this.usage === DynamicDrawUsage; - // Blob URL - if ( /^blob:.*$/i.test( url ) ) return url; + }, + set: function ( /* value */ ) { - // Relative URL - return path + url; + console.warn( 'THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead.' ); + this.setUsage( DynamicDrawUsage ); -} + } + } -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material - */ -function createDefaultMaterial$1( cache ) { +} ); - if ( cache[ 'DefaultMaterial' ] === undefined ) { +BufferAttribute.prototype.setDynamic = function ( value ) { - cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { - color: 0xFFFFFF, - emissive: 0x000000, - metalness: 1, - roughness: 1, - transparent: false, - depthTest: true, - side: FrontSide - } ); + console.warn( 'THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - } +}; - return cache[ 'DefaultMaterial' ]; +BufferAttribute.prototype.copyIndicesArray = function ( /* indices */ ) { -} + console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); -function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { +}, - // Add unknown glTF extensions to an object's userData. +BufferAttribute.prototype.setArray = function ( /* array */ ) { - for ( const name in objectDef.extensions ) { + console.error( 'THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - if ( knownExtensions[ name ] === undefined ) { +}; - object.userData.gltfExtensions = object.userData.gltfExtensions || {}; - object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; +// - } +BufferGeometry.prototype.addIndex = function ( index ) { - } + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); -} +}; -/** - * @param {Object3D|Material|BufferGeometry} object - * @param {GLTF.definition} gltfDef - */ -function assignExtrasToUserData( object, gltfDef ) { +BufferGeometry.prototype.addAttribute = function ( name, attribute ) { - if ( gltfDef.extras !== undefined ) { + console.warn( 'THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute().' ); - if ( typeof gltfDef.extras === 'object' ) { + if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { - Object.assign( object.userData, gltfDef.extras ); + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - } else { + return this.setAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); + } - } + if ( name === 'index' ) { - } + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); -} + return this; -/** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets - * - * @param {BufferGeometry} geometry - * @param {Array} targets - * @param {GLTFParser} parser - * @return {Promise} - */ -function addMorphTargets( geometry, targets, parser ) { + } - let hasMorphPosition = false; - let hasMorphNormal = false; + return this.setAttribute( name, attribute ); - for ( let i = 0, il = targets.length; i < il; i ++ ) { +}; - const target = targets[ i ]; +BufferGeometry.prototype.addDrawCall = function ( start, count, indexOffset ) { - if ( target.POSITION !== undefined ) hasMorphPosition = true; - if ( target.NORMAL !== undefined ) hasMorphNormal = true; + if ( indexOffset !== undefined ) { - if ( hasMorphPosition && hasMorphNormal ) break; + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); } - if ( ! hasMorphPosition && ! hasMorphNormal ) return Promise.resolve( geometry ); + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); - const pendingPositionAccessors = []; - const pendingNormalAccessors = []; +}; - for ( let i = 0, il = targets.length; i < il; i ++ ) { +BufferGeometry.prototype.clearDrawCalls = function () { - const target = targets[ i ]; + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); - if ( hasMorphPosition ) { +}; - const pendingAccessor = target.POSITION !== undefined - ? parser.getDependency( 'accessor', target.POSITION ) - : geometry.attributes.position; +BufferGeometry.prototype.computeOffsets = function () { - pendingPositionAccessors.push( pendingAccessor ); + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); - } +}; - if ( hasMorphNormal ) { +BufferGeometry.prototype.removeAttribute = function ( name ) { - const pendingAccessor = target.NORMAL !== undefined - ? parser.getDependency( 'accessor', target.NORMAL ) - : geometry.attributes.normal; + console.warn( 'THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute().' ); - pendingNormalAccessors.push( pendingAccessor ); + return this.deleteAttribute( name ); - } +}; - } +BufferGeometry.prototype.applyMatrix = function ( matrix ) { - return Promise.all( [ - Promise.all( pendingPositionAccessors ), - Promise.all( pendingNormalAccessors ) - ] ).then( function ( accessors ) { + console.warn( 'THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4().' ); + return this.applyMatrix4( matrix ); - const morphPositions = accessors[ 0 ]; - const morphNormals = accessors[ 1 ]; +}; - if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; - if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; - geometry.morphTargetsRelative = true; +Object.defineProperties( BufferGeometry.prototype, { - return geometry; + drawcalls: { + get: function () { - } ); + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; -} + } + }, + offsets: { + get: function () { -/** - * @param {Mesh} mesh - * @param {GLTF.Mesh} meshDef - */ -function updateMorphTargets( mesh, meshDef ) { + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; - mesh.updateMorphTargets(); + } + } - if ( meshDef.weights !== undefined ) { +} ); - for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { +InterleavedBuffer.prototype.setDynamic = function ( value ) { - mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; + console.warn( 'THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead.' ); + this.setUsage( value === true ? DynamicDrawUsage : StaticDrawUsage ); + return this; - } +}; - } +InterleavedBuffer.prototype.setArray = function ( /* array */ ) { - // .extras has user-defined data, so check that .extras.targetNames is an array. - if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { + console.error( 'THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers' ); - const targetNames = meshDef.extras.targetNames; +}; - if ( mesh.morphTargetInfluences.length === targetNames.length ) { +// - mesh.morphTargetDictionary = {}; +ExtrudeGeometry.prototype.getArrays = function () { - for ( let i = 0, il = targetNames.length; i < il; i ++ ) { + console.error( 'THREE.ExtrudeGeometry: .getArrays() has been removed.' ); - mesh.morphTargetDictionary[ targetNames[ i ] ] = i; +}; - } +ExtrudeGeometry.prototype.addShapeList = function () { - } else { + console.error( 'THREE.ExtrudeGeometry: .addShapeList() has been removed.' ); - console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); +}; - } +ExtrudeGeometry.prototype.addShape = function () { - } + console.error( 'THREE.ExtrudeGeometry: .addShape() has been removed.' ); -} +}; -function createPrimitiveKey( primitiveDef ) { +// - const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; - let geometryKey; +Scene.prototype.dispose = function () { - if ( dracoExtension ) { + console.error( 'THREE.Scene: .dispose() has been removed.' ); - geometryKey = 'draco:' + dracoExtension.bufferView - + ':' + dracoExtension.indices - + ':' + createAttributesKey( dracoExtension.attributes ); +}; - } else { +// - geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; +Object.defineProperties( Material.prototype, { - } + wrapAround: { + get: function () { - return geometryKey; + console.warn( 'THREE.Material: .wrapAround has been removed.' ); -} + }, + set: function () { -function createAttributesKey( attributes ) { + console.warn( 'THREE.Material: .wrapAround has been removed.' ); - let attributesKey = ''; + } + }, - const keys = Object.keys( attributes ).sort(); + overdraw: { + get: function () { - for ( let i = 0, il = keys.length; i < il; i ++ ) { + console.warn( 'THREE.Material: .overdraw has been removed.' ); - attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; + }, + set: function () { - } + console.warn( 'THREE.Material: .overdraw has been removed.' ); - return attributesKey; + } + }, -} + wrapRGB: { + get: function () { -function getNormalizedComponentScale( constructor ) { + console.warn( 'THREE.Material: .wrapRGB has been removed.' ); + return new Color(); - // Reference: - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data + } + }, - switch ( constructor ) { + shading: { + get: function () { - case Int8Array: - return 1 / 127; + console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - case Uint8Array: - return 1 / 255; + }, + set: function ( value ) { - case Int16Array: - return 1 / 32767; + console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.flatShading = ( value === FlatShading ); - case Uint16Array: - return 1 / 65535; + } + }, - default: - throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); + stencilMask: { + get: function () { - } + console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); + return this.stencilFuncMask; -} + }, + set: function ( value ) { -/* GLTF PARSER */ + console.warn( 'THREE.' + this.type + ': .stencilMask has been removed. Use .stencilFuncMask instead.' ); + this.stencilFuncMask = value; -class GLTFParser$1 { + } + }, - constructor( json = {}, options = {} ) { + vertexTangents: { + get: function () { - this.json = json; - this.extensions = {}; - this.plugins = {}; - this.options = options; + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - // loader object cache - this.cache = new GLTFRegistry$1(); + }, + set: function () { - // associations between Three.js objects and glTF elements - this.associations = new Map(); + console.warn( 'THREE.' + this.type + ': .vertexTangents has been removed.' ); - // BufferGeometry caching - this.primitiveCache = {}; + } + }, - // Object3D instance caches - this.meshCache = { refs: {}, uses: {} }; - this.cameraCache = { refs: {}, uses: {} }; - this.lightCache = { refs: {}, uses: {} }; +} ); - // Track node names, to ensure no duplicates - this.nodeNamesUsed = {}; +Object.defineProperties( ShaderMaterial.prototype, { - // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the - // expensive work of uploading a texture to the GPU off the main thread. - if ( typeof createImageBitmap !== 'undefined' && /Firefox/.test( navigator.userAgent ) === false ) { + derivatives: { + get: function () { - this.textureLoader = new ImageBitmapLoader( this.options.manager ); + console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + return this.extensions.derivatives; - } else { + }, + set: function ( value ) { - this.textureLoader = new TextureLoader( this.options.manager ); + console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + this.extensions.derivatives = value; } + } - this.textureLoader.setCrossOrigin( this.options.crossOrigin ); - this.textureLoader.setRequestHeader( this.options.requestHeader ); - - this.fileLoader = new FileLoader( this.options.manager ); - this.fileLoader.setResponseType( 'arraybuffer' ); +} ); - if ( this.options.crossOrigin === 'use-credentials' ) { +// - this.fileLoader.setWithCredentials( true ); +WebGLRenderer.prototype.clearTarget = function ( renderTarget, color, depth, stencil ) { - } + console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); - } +}; - setExtensions( extensions ) { +WebGLRenderer.prototype.animate = function ( callback ) { - this.extensions = extensions; + console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); + this.setAnimationLoop( callback ); - } +}; - setPlugins( plugins ) { +WebGLRenderer.prototype.getCurrentRenderTarget = function () { - this.plugins = plugins; + console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); + return this.getRenderTarget(); - } +}; - parse( onLoad, onError ) { +WebGLRenderer.prototype.getMaxAnisotropy = function () { - const parser = this; - const json = this.json; - const extensions = this.extensions; + console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); + return this.capabilities.getMaxAnisotropy(); - // Clear the loader cache - this.cache.removeAll(); +}; - // Mark the special nodes/meshes in json for efficient parse - this._invokeAll( function ( ext ) { +WebGLRenderer.prototype.getPrecision = function () { - return ext._markDefs && ext._markDefs(); + console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); + return this.capabilities.precision; - } ); +}; - Promise.all( this._invokeAll( function ( ext ) { +WebGLRenderer.prototype.resetGLState = function () { - return ext.beforeRoot && ext.beforeRoot(); + console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); + return this.state.reset(); - } ) ).then( function () { +}; - return Promise.all( [ +WebGLRenderer.prototype.supportsFloatTextures = function () { - parser.getDependencies( 'scene' ), - parser.getDependencies( 'animation' ), - parser.getDependencies( 'camera' ), + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return this.extensions.get( 'OES_texture_float' ); - ] ); +}; - } ).then( function ( dependencies ) { +WebGLRenderer.prototype.supportsHalfFloatTextures = function () { - const result = { - scene: dependencies[ 0 ][ json.scene || 0 ], - scenes: dependencies[ 0 ], - animations: dependencies[ 1 ], - cameras: dependencies[ 2 ], - asset: json.asset, - parser: parser, - userData: {} - }; + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return this.extensions.get( 'OES_texture_half_float' ); - addUnknownExtensionsToUserData( extensions, result, json ); +}; - assignExtrasToUserData( result, json ); +WebGLRenderer.prototype.supportsStandardDerivatives = function () { - Promise.all( parser._invokeAll( function ( ext ) { + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return this.extensions.get( 'OES_standard_derivatives' ); - return ext.afterRoot && ext.afterRoot( result ); +}; - } ) ).then( function () { +WebGLRenderer.prototype.supportsCompressedTextureS3TC = function () { - onLoad( result ); + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); - } ); +}; - } ).catch( onError ); +WebGLRenderer.prototype.supportsCompressedTexturePVRTC = function () { - } + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - /** - * Marks the special nodes/meshes in json for efficient parse. - */ - _markDefs() { +}; - const nodeDefs = this.json.nodes || []; - const skinDefs = this.json.skins || []; - const meshDefs = this.json.meshes || []; +WebGLRenderer.prototype.supportsBlendMinMax = function () { - // Nothing in the node definition indicates whether it is a Bone or an - // Object3D. Use the skins' joint references to mark bones. - for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return this.extensions.get( 'EXT_blend_minmax' ); - const joints = skinDefs[ skinIndex ].joints; +}; - for ( let i = 0, il = joints.length; i < il; i ++ ) { +WebGLRenderer.prototype.supportsVertexTextures = function () { - nodeDefs[ joints[ i ] ].isBone = true; + console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); + return this.capabilities.vertexTextures; - } +}; - } +WebGLRenderer.prototype.supportsInstancedArrays = function () { - // Iterate over all nodes, marking references to shared resources, - // as well as skeleton joints. - for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return this.extensions.get( 'ANGLE_instanced_arrays' ); - const nodeDef = nodeDefs[ nodeIndex ]; +}; - if ( nodeDef.mesh !== undefined ) { +WebGLRenderer.prototype.enableScissorTest = function ( boolean ) { - this._addNodeRef( this.meshCache, nodeDef.mesh ); + console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); + this.setScissorTest( boolean ); - // Nothing in the mesh definition indicates whether it is - // a SkinnedMesh or Mesh. Use the node's mesh reference - // to mark SkinnedMesh if node has skin. - if ( nodeDef.skin !== undefined ) { +}; - meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; +WebGLRenderer.prototype.initMaterial = function () { - } + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - } +}; - if ( nodeDef.camera !== undefined ) { +WebGLRenderer.prototype.addPrePlugin = function () { - this._addNodeRef( this.cameraCache, nodeDef.camera ); + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - } +}; - } +WebGLRenderer.prototype.addPostPlugin = function () { - } + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - /** - * Counts references to shared node / Object3D resources. These resources - * can be reused, or "instantiated", at multiple nodes in the scene - * hierarchy. Mesh, Camera, and Light instances are instantiated and must - * be marked. Non-scenegraph resources (like Materials, Geometries, and - * Textures) can be reused directly and are not marked here. - * - * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. - */ - _addNodeRef( cache, index ) { +}; - if ( index === undefined ) return; +WebGLRenderer.prototype.updateShadowMap = function () { - if ( cache.refs[ index ] === undefined ) { + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - cache.refs[ index ] = cache.uses[ index ] = 0; +}; - } +WebGLRenderer.prototype.setFaceCulling = function () { - cache.refs[ index ] ++; + console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); - } +}; - /** Returns a reference to a shared resource, cloning it if necessary. */ - _getNodeRef( cache, index, object ) { +WebGLRenderer.prototype.allocTextureUnit = function () { - if ( cache.refs[ index ] <= 1 ) return object; + console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); - const ref = object.clone(); +}; - ref.name += '_instance_' + ( cache.uses[ index ] ++ ); +WebGLRenderer.prototype.setTexture = function () { - return ref; + console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); - } +}; - _invokeOne( func ) { +WebGLRenderer.prototype.setTexture2D = function () { - const extensions = Object.values( this.plugins ); - extensions.push( this ); + console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); - for ( let i = 0; i < extensions.length; i ++ ) { +}; - const result = func( extensions[ i ] ); +WebGLRenderer.prototype.setTextureCube = function () { - if ( result ) return result; + console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); - } +}; - return null; +WebGLRenderer.prototype.getActiveMipMapLevel = function () { - } + console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); + return this.getActiveMipmapLevel(); - _invokeAll( func ) { +}; - const extensions = Object.values( this.plugins ); - extensions.unshift( this ); +Object.defineProperties( WebGLRenderer.prototype, { - const pending = []; + shadowMapEnabled: { + get: function () { - for ( let i = 0; i < extensions.length; i ++ ) { + return this.shadowMap.enabled; - const result = func( extensions[ i ] ); + }, + set: function ( value ) { - if ( result ) pending.push( result ); + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + this.shadowMap.enabled = value; } + }, + shadowMapType: { + get: function () { - return pending; - - } + return this.shadowMap.type; - /** - * Requests the specified dependency asynchronously, with caching. - * @param {string} type - * @param {number} index - * @return {Promise} - */ - getDependency( type, index ) { + }, + set: function ( value ) { - const cacheKey = type + ':' + index; - let dependency = this.cache.get( cacheKey ); + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + this.shadowMap.type = value; - if ( ! dependency ) { + } + }, + shadowMapCullFace: { + get: function () { - switch ( type ) { + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); + return undefined; - case 'scene': - dependency = this.loadScene( index ); - break; + }, + set: function ( /* value */ ) { - case 'node': - dependency = this.loadNode( index ); - break; + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - case 'mesh': - dependency = this._invokeOne( function ( ext ) { + } + }, + context: { + get: function () { - return ext.loadMesh && ext.loadMesh( index ); + console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); + return this.getContext(); - } ); - break; + } + }, + vr: { + get: function () { - case 'accessor': - dependency = this.loadAccessor( index ); - break; + console.warn( 'THREE.WebGLRenderer: .vr has been renamed to .xr' ); + return this.xr; - case 'bufferView': - dependency = this._invokeOne( function ( ext ) { + } + }, + gammaInput: { + get: function () { - return ext.loadBufferView && ext.loadBufferView( index ); + console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); + return false; - } ); - break; + }, + set: function () { - case 'buffer': - dependency = this.loadBuffer( index ); - break; + console.warn( 'THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.' ); - case 'material': - dependency = this._invokeOne( function ( ext ) { + } + }, + gammaOutput: { + get: function () { - return ext.loadMaterial && ext.loadMaterial( index ); + console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); + return false; - } ); - break; + }, + set: function ( value ) { - case 'texture': - dependency = this._invokeOne( function ( ext ) { + console.warn( 'THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead.' ); + this.outputEncoding = ( value === true ) ? sRGBEncoding : LinearEncoding; - return ext.loadTexture && ext.loadTexture( index ); + } + }, + toneMappingWhitePoint: { + get: function () { - } ); - break; + console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); + return 1.0; - case 'skin': - dependency = this.loadSkin( index ); - break; + }, + set: function () { - case 'animation': - dependency = this.loadAnimation( index ); - break; + console.warn( 'THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.' ); - case 'camera': - dependency = this.loadCamera( index ); - break; + } + }, + gammaFactor: { + get: function () { - default: - throw new Error( 'Unknown type: ' + type ); + console.warn( 'THREE.WebGLRenderer: .gammaFactor has been removed.' ); + return 2; - } + }, + set: function () { - this.cache.add( cacheKey, dependency ); + console.warn( 'THREE.WebGLRenderer: .gammaFactor has been removed.' ); } - - return dependency; - } +} ); - /** - * Requests all dependencies of the specified type asynchronously, with caching. - * @param {string} type - * @return {Promise>} - */ - getDependencies( type ) { +Object.defineProperties( WebGLShadowMap.prototype, { - let dependencies = this.cache.get( type ); + cullFace: { + get: function () { - if ( ! dependencies ) { + console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); + return undefined; - const parser = this; - const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; + }, + set: function ( /* cullFace */ ) { - dependencies = Promise.all( defs.map( function ( def, index ) { + console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return parser.getDependency( type, index ); + } + }, + renderReverseSided: { + get: function () { - } ) ); + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); + return undefined; - this.cache.add( type, dependencies ); + }, + set: function () { + + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); } + }, + renderSingleSided: { + get: function () { - return dependencies; + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); + return undefined; - } + }, + set: function () { - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferIndex - * @return {Promise} - */ - loadBuffer( bufferIndex ) { + console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - const bufferDef = this.json.buffers[ bufferIndex ]; - const loader = this.fileLoader; + } + } - if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { +} ); - throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); +// - } +Object.defineProperties( WebGLRenderTarget.prototype, { - // If present, GLB container is required to be the first buffer. - if ( bufferDef.uri === undefined && bufferIndex === 0 ) { + wrapS: { + get: function () { - return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + return this.texture.wrapS; - } + }, + set: function ( value ) { - const options = this.options; + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + this.texture.wrapS = value; - return new Promise( function ( resolve, reject ) { + } + }, + wrapT: { + get: function () { - loader.load( resolveURL$1( bufferDef.uri, options.path ), resolve, undefined, function () { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + return this.texture.wrapT; - reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); + }, + set: function ( value ) { - } ); + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + this.texture.wrapT = value; - } ); + } + }, + magFilter: { + get: function () { - } + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + return this.texture.magFilter; - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views - * @param {number} bufferViewIndex - * @return {Promise} - */ - loadBufferView( bufferViewIndex ) { + }, + set: function ( value ) { - const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + this.texture.magFilter = value; - return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { + } + }, + minFilter: { + get: function () { - const byteLength = bufferViewDef.byteLength || 0; - const byteOffset = bufferViewDef.byteOffset || 0; - return buffer.slice( byteOffset, byteOffset + byteLength ); + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + return this.texture.minFilter; - } ); + }, + set: function ( value ) { - } + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + this.texture.minFilter = value; - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors - * @param {number} accessorIndex - * @return {Promise} - */ - loadAccessor( accessorIndex ) { + } + }, + anisotropy: { + get: function () { - const parser = this; - const json = this.json; + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + return this.texture.anisotropy; - const accessorDef = this.json.accessors[ accessorIndex ]; + }, + set: function ( value ) { - if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { - - // Ignore empty accessors, which may be used to declare runtime - // information about attributes coming from another source (e.g. Draco - // compression extension). - return Promise.resolve( null ); + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + this.texture.anisotropy = value; } + }, + offset: { + get: function () { - const pendingBufferViews = []; - - if ( accessorDef.bufferView !== undefined ) { - - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + return this.texture.offset; - } else { + }, + set: function ( value ) { - pendingBufferViews.push( null ); + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + this.texture.offset = value; } + }, + repeat: { + get: function () { - if ( accessorDef.sparse !== undefined ) { + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + return this.texture.repeat; - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); - pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); + }, + set: function ( value ) { - } + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + this.texture.repeat = value; - return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { + } + }, + format: { + get: function () { - const bufferView = bufferViews[ 0 ]; + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + return this.texture.format; - const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; - const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + }, + set: function ( value ) { - // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. - const elementBytes = TypedArray.BYTES_PER_ELEMENT; - const itemBytes = elementBytes * itemSize; - const byteOffset = accessorDef.byteOffset || 0; - const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; - const normalized = accessorDef.normalized === true; - let array, bufferAttribute; + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + this.texture.format = value; - // The buffer is not interleaved if the stride is the item size in bytes. - if ( byteStride && byteStride !== itemBytes ) { + } + }, + type: { + get: function () { - // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer - // This makes sure that IBA.count reflects accessor.count properly - const ibSlice = Math.floor( byteOffset / byteStride ); - const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; - let ib = parser.cache.get( ibCacheKey ); + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + return this.texture.type; - if ( ! ib ) { + }, + set: function ( value ) { - array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + this.texture.type = value; - // Integer parameters to IB/IBA are in array elements, not bytes. - ib = new InterleavedBuffer( array, byteStride / elementBytes ); + } + }, + generateMipmaps: { + get: function () { - parser.cache.add( ibCacheKey, ib ); + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + return this.texture.generateMipmaps; - } + }, + set: function ( value ) { - bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + this.texture.generateMipmaps = value; - } else { + } + } - if ( bufferView === null ) { +} ); - array = new TypedArray( accessorDef.count * itemSize ); +// - } else { +Audio.prototype.load = function ( file ) { - array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); + console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); + const scope = this; + const audioLoader = new AudioLoader(); + audioLoader.load( file, function ( buffer ) { - } + scope.setBuffer( buffer ); - bufferAttribute = new BufferAttribute( array, itemSize, normalized ); + } ); + return this; - } +}; - // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors - if ( accessorDef.sparse !== undefined ) { +// - const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; - const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; +CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { - const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; - const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; + console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); + return this.update( renderer, scene ); - const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); - const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); +}; - if ( bufferView !== null ) { +CubeCamera.prototype.clear = function ( renderer, color, depth, stencil ) { - // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. - bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); + console.warn( 'THREE.CubeCamera: .clear() is now .renderTarget.clear().' ); + return this.renderTarget.clear( renderer, color, depth, stencil ); - } +}; - for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { +ImageUtils.crossOrigin = undefined; - const index = sparseIndices[ i ]; +ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); - if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); - if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); - if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); - if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); + console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - } + const loader = new TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - } + const texture = loader.load( url, onLoad, undefined, onError ); - return bufferAttribute; + if ( mapping ) texture.mapping = mapping; - } ); + return texture; - } +}; - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures - * @param {number} textureIndex - * @return {Promise} - */ - loadTexture( textureIndex ) { +ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - const json = this.json; - const options = this.options; - const textureDef = json.textures[ textureIndex ]; - const source = json.images[ textureDef.source ]; + console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - let loader = this.textureLoader; + const loader = new CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - if ( source.uri ) { + const texture = loader.load( urls, onLoad, undefined, onError ); - const handler = options.manager.getHandler( source.uri ); - if ( handler !== null ) loader = handler; + if ( mapping ) texture.mapping = mapping; - } + return texture; - return this.loadTextureImage( textureIndex, source, loader ); +}; - } +ImageUtils.loadCompressedTexture = function () { - loadTextureImage( textureIndex, source, loader ) { + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - const parser = this; - const json = this.json; - const options = this.options; +}; - const textureDef = json.textures[ textureIndex ]; +ImageUtils.loadCompressedTextureCube = function () { - const URL = self.URL || self.webkitURL; + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); - let sourceURI = source.uri; - let isObjectURL = false; - let hasAlpha = true; +}; - if ( source.mimeType === 'image/jpeg' ) hasAlpha = false; +if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - if ( source.bufferView !== undefined ) { + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { + revision: REVISION, + } } ) ); - // Load binary image data from bufferView, if provided. +} - sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) { +if ( typeof window !== 'undefined' ) { - if ( source.mimeType === 'image/png' ) { + if ( window.__THREE__ ) { - // Inspect the PNG 'IHDR' chunk to determine whether the image could have an - // alpha channel. This check is conservative — the image could have an alpha - // channel with all values == 1, and the indexed type (colorType == 3) only - // sometimes contains alpha. - // - // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header - const colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false ); - hasAlpha = colorType === 6 || colorType === 4 || colorType === 3; + console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); - } + } else { - isObjectURL = true; - const blob = new Blob( [ bufferView ], { type: source.mimeType } ); - sourceURI = URL.createObjectURL( blob ); - return sourceURI; + window.__THREE__ = REVISION; - } ); + } - } else if ( source.uri === undefined ) { +} - throw new Error( 'THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView' ); +const _taskCache = new WeakMap(); - } +class DRACOLoader extends Loader { - return Promise.resolve( sourceURI ).then( function ( sourceURI ) { + constructor( manager ) { - return new Promise( function ( resolve, reject ) { + super( manager ); - let onLoad = resolve; + this.decoderPath = ''; + this.decoderConfig = {}; + this.decoderBinary = null; + this.decoderPending = null; - if ( loader.isImageBitmapLoader === true ) { + this.workerLimit = 4; + this.workerPool = []; + this.workerNextTaskID = 1; + this.workerSourceURL = ''; - onLoad = function ( imageBitmap ) { + this.defaultAttributeIDs = { + position: 'POSITION', + normal: 'NORMAL', + color: 'COLOR', + uv: 'TEX_COORD' + }; + this.defaultAttributeTypes = { + position: 'Float32Array', + normal: 'Float32Array', + color: 'Float32Array', + uv: 'Float32Array' + }; - resolve( new CanvasTexture( imageBitmap ) ); + } - }; + setDecoderPath( path ) { - } + this.decoderPath = path; - loader.load( resolveURL$1( sourceURI, options.path ), onLoad, undefined, reject ); + return this; - } ); + } - } ).then( function ( texture ) { + setDecoderConfig( config ) { - // Clean up resources and configure Texture. + this.decoderConfig = config; - if ( isObjectURL === true ) { + return this; - URL.revokeObjectURL( sourceURI ); + } - } + setWorkerLimit( workerLimit ) { - texture.flipY = false; + this.workerLimit = workerLimit; - if ( textureDef.name ) texture.name = textureDef.name; + return this; - // When there is definitely no alpha channel in the texture, set RGBFormat to save space. - if ( ! hasAlpha ) texture.format = RGBFormat; + } - const samplers = json.samplers || {}; - const sampler = samplers[ textureDef.sampler ] || {}; + load( url, onLoad, onProgress, onError ) { - texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || LinearFilter; - texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || LinearMipmapLinearFilter; - texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || RepeatWrapping; - texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || RepeatWrapping; + const loader = new FileLoader( this.manager ); - parser.associations.set( texture, { - type: 'textures', - index: textureIndex - } ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); - return texture; + loader.load( url, ( buffer ) => { - } ); + const taskConfig = { + attributeIDs: this.defaultAttributeIDs, + attributeTypes: this.defaultAttributeTypes, + useUniqueIDs: false + }; - } + this.decodeGeometry( buffer, taskConfig ) + .then( onLoad ) + .catch( onError ); - /** - * Asynchronously assigns a texture to the given material parameters. - * @param {Object} materialParams - * @param {string} mapName - * @param {Object} mapDef - * @return {Promise} - */ - assignTexture( materialParams, mapName, mapDef ) { + }, onProgress, onError ); - const parser = this; + } - return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { + /** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */ + decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) { - // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured - // However, we will copy UV set 0 to UV set 1 on demand for aoMap - if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { + const taskConfig = { + attributeIDs: attributeIDs || this.defaultAttributeIDs, + attributeTypes: attributeTypes || this.defaultAttributeTypes, + useUniqueIDs: !! attributeIDs + }; - console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); + this.decodeGeometry( buffer, taskConfig ).then( callback ); - } + } - if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + decodeGeometry( buffer, taskConfig ) { - const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + // TODO: For backward-compatibility, support 'attributeTypes' objects containing + // references (rather than names) to typed array constructors. These must be + // serialized before sending them to the worker. + for ( const attribute in taskConfig.attributeTypes ) { - if ( transform ) { + const type = taskConfig.attributeTypes[ attribute ]; - const gltfReference = parser.associations.get( texture ); - texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); - parser.associations.set( texture, gltfReference ); + if ( type.BYTES_PER_ELEMENT !== undefined ) { - } + taskConfig.attributeTypes[ attribute ] = type.name; } - materialParams[ mapName ] = texture; - - } ); + } - } + // - /** - * Assigns final material to a Mesh, Line, or Points instance. The instance - * already has a material (generated from the glTF material options alone) - * but reuse of the same glTF material may require multiple threejs materials - * to accommodate different primitive types, defines, etc. New materials will - * be created if necessary, and reused from a cache. - * @param {Object3D} mesh Mesh, Line, or Points instance. - */ - assignFinalMaterial( mesh ) { + const taskKey = JSON.stringify( taskConfig ); - const geometry = mesh.geometry; - let material = mesh.material; + // Check for an existing task using this buffer. A transferred buffer cannot be transferred + // again from this thread. + if ( _taskCache.has( buffer ) ) { - const useVertexTangents = geometry.attributes.tangent !== undefined; - const useVertexColors = geometry.attributes.color !== undefined; - const useFlatShading = geometry.attributes.normal === undefined; - const useSkinning = mesh.isSkinnedMesh === true; - const useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0; - const useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined; + const cachedTask = _taskCache.get( buffer ); - if ( mesh.isPoints ) { + if ( cachedTask.key === taskKey ) { - const cacheKey = 'PointsMaterial:' + material.uuid; + return cachedTask.promise; - let pointsMaterial = this.cache.get( cacheKey ); + } else if ( buffer.byteLength === 0 ) { - if ( ! pointsMaterial ) { + // Technically, it would be possible to wait for the previous task to complete, + // transfer the buffer back, and decode again with the second configuration. That + // is complex, and I don't know of any reason to decode a Draco buffer twice in + // different ways, so this is left unimplemented. + throw new Error( - pointsMaterial = new PointsMaterial(); - Material.prototype.copy.call( pointsMaterial, material ); - pointsMaterial.color.copy( material.color ); - pointsMaterial.map = material.map; - pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px + 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + + 'settings. Buffer has already been transferred.' - this.cache.add( cacheKey, pointsMaterial ); + ); } - material = pointsMaterial; + } - } else if ( mesh.isLine ) { + // - const cacheKey = 'LineBasicMaterial:' + material.uuid; + let worker; + const taskID = this.workerNextTaskID ++; + const taskCost = buffer.byteLength; - let lineMaterial = this.cache.get( cacheKey ); + // Obtain a worker and assign a task, and construct a geometry instance + // when the task completes. + const geometryPending = this._getWorker( taskID, taskCost ) + .then( ( _worker ) => { - if ( ! lineMaterial ) { + worker = _worker; - lineMaterial = new LineBasicMaterial(); - Material.prototype.copy.call( lineMaterial, material ); - lineMaterial.color.copy( material.color ); + return new Promise( ( resolve, reject ) => { - this.cache.add( cacheKey, lineMaterial ); + worker._callbacks[ taskID ] = { resolve, reject }; - } + worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] ); - material = lineMaterial; + // this.debug(); - } + } ); - // Clone the material if it will be modified - if ( useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets ) { + } ) + .then( ( message ) => this._createGeometry( message.geometry ) ); - let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; + // Remove task from the task list. + // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) + geometryPending + .catch( () => true ) + .then( () => { - if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; - if ( useSkinning ) cacheKey += 'skinning:'; - if ( useVertexTangents ) cacheKey += 'vertex-tangents:'; - if ( useVertexColors ) cacheKey += 'vertex-colors:'; - if ( useFlatShading ) cacheKey += 'flat-shading:'; - if ( useMorphTargets ) cacheKey += 'morph-targets:'; - if ( useMorphNormals ) cacheKey += 'morph-normals:'; + if ( worker && taskID ) { - let cachedMaterial = this.cache.get( cacheKey ); + this._releaseTask( worker, taskID ); - if ( ! cachedMaterial ) { + // this.debug(); - cachedMaterial = material.clone(); + } - if ( useSkinning ) cachedMaterial.skinning = true; - if ( useVertexColors ) cachedMaterial.vertexColors = true; - if ( useFlatShading ) cachedMaterial.flatShading = true; - if ( useMorphTargets ) cachedMaterial.morphTargets = true; - if ( useMorphNormals ) cachedMaterial.morphNormals = true; + } ); + + // Cache the task result. + _taskCache.set( buffer, { - if ( useVertexTangents ) { + key: taskKey, + promise: geometryPending - cachedMaterial.vertexTangents = true; + } ); - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; - if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; + return geometryPending; - } + } - this.cache.add( cacheKey, cachedMaterial ); + _createGeometry( geometryData ) { - this.associations.set( cachedMaterial, this.associations.get( material ) ); + const geometry = new BufferGeometry(); - } + if ( geometryData.index ) { - material = cachedMaterial; + geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) ); } - // workarounds for mesh and geometry + for ( let i = 0; i < geometryData.attributes.length; i ++ ) { - if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { + const attribute = geometryData.attributes[ i ]; + const name = attribute.name; + const array = attribute.array; + const itemSize = attribute.itemSize; - geometry.setAttribute( 'uv2', geometry.attributes.uv ); + geometry.setAttribute( name, new BufferAttribute( array, itemSize ) ); } - mesh.material = material; + return geometry; } - getMaterialType( /* materialIndex */ ) { + _loadLibrary( url, responseType ) { - return MeshStandardMaterial; + const loader = new FileLoader( this.manager ); + loader.setPath( this.decoderPath ); + loader.setResponseType( responseType ); + loader.setWithCredentials( this.withCredentials ); - } + return new Promise( ( resolve, reject ) => { - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials - * @param {number} materialIndex - * @return {Promise} - */ - loadMaterial( materialIndex ) { + loader.load( url, resolve, undefined, reject ); - const parser = this; - const json = this.json; - const extensions = this.extensions; - const materialDef = json.materials[ materialIndex ]; + } ); - let materialType; - const materialParams = {}; - const materialExtensions = materialDef.extensions || {}; + } - const pending = []; + preload() { - if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { + this._initDecoder(); - const sgExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; - materialType = sgExtension.getMaterialType(); - pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); + return this; - } else if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + } - const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; - materialType = kmuExtension.getMaterialType(); - pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); + _initDecoder() { - } else { + if ( this.decoderPending ) return this.decoderPending; - // Specification: - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material + const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js'; + const librariesPending = []; - const metallicRoughness = materialDef.pbrMetallicRoughness || {}; + if ( useJS ) { - materialParams.color = new Color( 1.0, 1.0, 1.0 ); - materialParams.opacity = 1.0; + librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) ); - if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + } else { - const array = metallicRoughness.baseColorFactor; + librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) ); + librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) ); - materialParams.color.fromArray( array ); - materialParams.opacity = array[ 3 ]; + } - } + this.decoderPending = Promise.all( librariesPending ) + .then( ( libraries ) => { - if ( metallicRoughness.baseColorTexture !== undefined ) { + const jsContent = libraries[ 0 ]; - pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) ); + if ( ! useJS ) { - } + this.decoderConfig.wasmBinary = libraries[ 1 ]; - materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; - materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; - - if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { - - pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); - pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); + } - } + const fn = DRACOWorker.toString(); - materialType = this._invokeOne( function ( ext ) { + const body = [ + '/* draco decoder */', + jsContent, + '', + '/* worker */', + fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) + ].join( '\n' ); - return ext.getMaterialType && ext.getMaterialType( materialIndex ); + this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) ); } ); - pending.push( Promise.all( this._invokeAll( function ( ext ) { - - return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); + return this.decoderPending; - } ) ) ); + } - } + _getWorker( taskID, taskCost ) { - if ( materialDef.doubleSided === true ) { + return this._initDecoder().then( () => { - materialParams.side = DoubleSide; + if ( this.workerPool.length < this.workerLimit ) { - } + const worker = new Worker( this.workerSourceURL ); - const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; + worker._callbacks = {}; + worker._taskCosts = {}; + worker._taskLoad = 0; - if ( alphaMode === ALPHA_MODES.BLEND ) { + worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } ); - materialParams.transparent = true; + worker.onmessage = function ( e ) { - // See: https://github.com/mrdoob/three.js/issues/17706 - materialParams.depthWrite = false; + const message = e.data; - } else { + switch ( message.type ) { - materialParams.transparent = false; + case 'decode': + worker._callbacks[ message.id ].resolve( message ); + break; - if ( alphaMode === ALPHA_MODES.MASK ) { + case 'error': + worker._callbacks[ message.id ].reject( message ); + break; - materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; + default: + console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' ); - } + } - } + }; - if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { + this.workerPool.push( worker ); - pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); + } else { - // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - materialParams.normalScale = new Vector2( 1, - 1 ); + this.workerPool.sort( function ( a, b ) { - if ( materialDef.normalTexture.scale !== undefined ) { + return a._taskLoad > b._taskLoad ? - 1 : 1; - materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale ); + } ); } - } + const worker = this.workerPool[ this.workerPool.length - 1 ]; + worker._taskCosts[ taskID ] = taskCost; + worker._taskLoad += taskCost; + return worker; - if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { + } ); - pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); + } - if ( materialDef.occlusionTexture.strength !== undefined ) { + _releaseTask( worker, taskID ) { - materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; + worker._taskLoad -= worker._taskCosts[ taskID ]; + delete worker._callbacks[ taskID ]; + delete worker._taskCosts[ taskID ]; - } + } - } + debug() { - if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { + console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) ); - materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); + } - } + dispose() { - if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { + for ( let i = 0; i < this.workerPool.length; ++ i ) { - pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture ) ); + this.workerPool[ i ].terminate(); } - return Promise.all( pending ).then( function () { + this.workerPool.length = 0; - let material; + return this; - if ( materialType === GLTFMeshStandardSGMaterial ) { + } - material = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); +} - } else { +/* WEB WORKER */ - material = new materialType( materialParams ); +function DRACOWorker() { - } + let decoderConfig; + let decoderPending; - if ( materialDef.name ) material.name = materialDef.name; + onmessage = function ( e ) { - // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding. - if ( material.map ) material.map.encoding = sRGBEncoding; - if ( material.emissiveMap ) material.emissiveMap.encoding = sRGBEncoding; + const message = e.data; - assignExtrasToUserData( material, materialDef ); + switch ( message.type ) { - parser.associations.set( material, { type: 'materials', index: materialIndex } ); + case 'init': + decoderConfig = message.decoderConfig; + decoderPending = new Promise( function ( resolve/*, reject*/ ) { - if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); + decoderConfig.onModuleLoaded = function ( draco ) { - return material; + // Module is Promise-like. Wrap before resolving to avoid loop. + resolve( { draco: draco } ); - } ); + }; - } + DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef - /** When Object3D instances are targeted by animation, they need unique names. */ - createUniqueName( originalName ) { + } ); + break; - const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); + case 'decode': + const buffer = message.buffer; + const taskConfig = message.taskConfig; + decoderPending.then( ( module ) => { - let name = sanitizedName; + const draco = module.draco; + const decoder = new draco.Decoder(); + const decoderBuffer = new draco.DecoderBuffer(); + decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength ); - for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { + try { - name = sanitizedName + '_' + i; + const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig ); - } + const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer ); - this.nodeNamesUsed[ name ] = true; + if ( geometry.index ) buffers.push( geometry.index.array.buffer ); - return name; + self.postMessage( { type: 'decode', id: message.id, geometry }, buffers ); - } + } catch ( error ) { - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry - * - * Creates BufferGeometries from primitives. - * - * @param {Array} primitives - * @return {Promise>} - */ - loadGeometries( primitives ) { + console.error( error ); - const parser = this; - const extensions = this.extensions; - const cache = this.primitiveCache; + self.postMessage( { type: 'error', id: message.id, error: error.message } ); - function createDracoPrimitive( primitive ) { + } finally { - return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] - .decodePrimitive( primitive, parser ) - .then( function ( geometry ) { + draco.destroy( decoderBuffer ); + draco.destroy( decoder ); - return addPrimitiveAttributes( geometry, primitive, parser ); + } } ); + break; } - const pending = []; + }; - for ( let i = 0, il = primitives.length; i < il; i ++ ) { + function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) { - const primitive = primitives[ i ]; - const cacheKey = createPrimitiveKey( primitive ); + const attributeIDs = taskConfig.attributeIDs; + const attributeTypes = taskConfig.attributeTypes; - // See if we've already created this geometry - const cached = cache[ cacheKey ]; + let dracoGeometry; + let decodingStatus; - if ( cached ) { + const geometryType = decoder.GetEncodedGeometryType( decoderBuffer ); - // Use the cached geometry if it exists - pending.push( cached.promise ); + if ( geometryType === draco.TRIANGULAR_MESH ) { - } else { + dracoGeometry = new draco.Mesh(); + decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry ); - let geometryPromise; + } else if ( geometryType === draco.POINT_CLOUD ) { - if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + dracoGeometry = new draco.PointCloud(); + decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry ); - // Use DRACO geometry if available - geometryPromise = createDracoPrimitive( primitive ); + } else { - } else { + throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' ); - // Otherwise create a new geometry - geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); + } - } + if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) { - // Cache this geometry - cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; + throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() ); - pending.push( geometryPromise ); + } - } + const geometry = { index: null, attributes: [] }; - } + // Gather all vertex attributes. + for ( const attributeName in attributeIDs ) { - return Promise.all( pending ); + const attributeType = self[ attributeTypes[ attributeName ] ]; - } + let attribute; + let attributeID; - /** - * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes - * @param {number} meshIndex - * @return {Promise} - */ - loadMesh( meshIndex ) { + // A Draco file may be created with default vertex attributes, whose attribute IDs + // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively, + // a Draco file may contain a custom set of attributes, identified by known unique + // IDs. glTF files always do the latter, and `.drc` files typically do the former. + if ( taskConfig.useUniqueIDs ) { - const parser = this; - const json = this.json; - const extensions = this.extensions; + attributeID = attributeIDs[ attributeName ]; + attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID ); - const meshDef = json.meshes[ meshIndex ]; - const primitives = meshDef.primitives; + } else { - const pending = []; + attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] ); - for ( let i = 0, il = primitives.length; i < il; i ++ ) { + if ( attributeID === - 1 ) continue; - const material = primitives[ i ].material === undefined - ? createDefaultMaterial$1( this.cache ) - : this.getDependency( 'material', primitives[ i ].material ); + attribute = decoder.GetAttribute( dracoGeometry, attributeID ); - pending.push( material ); + } + + geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) ); } - pending.push( parser.loadGeometries( primitives ) ); + // Add index. + if ( geometryType === draco.TRIANGULAR_MESH ) { - return Promise.all( pending ).then( function ( results ) { + geometry.index = decodeIndex( draco, decoder, dracoGeometry ); - const materials = results.slice( 0, results.length - 1 ); - const geometries = results[ results.length - 1 ]; + } - const meshes = []; + draco.destroy( dracoGeometry ); - for ( let i = 0, il = geometries.length; i < il; i ++ ) { + return geometry; - const geometry = geometries[ i ]; - const primitive = primitives[ i ]; + } - // 1. create Mesh + function decodeIndex( draco, decoder, dracoGeometry ) { - let mesh; + const numFaces = dracoGeometry.num_faces(); + const numIndices = numFaces * 3; + const byteLength = numIndices * 4; - const material = materials[ i ]; + const ptr = draco._malloc( byteLength ); + decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr ); + const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice(); + draco._free( ptr ); - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || - primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || - primitive.mode === undefined ) { + return { array: index, itemSize: 1 }; - // .isSkinnedMesh isn't in glTF spec. See ._markDefs() - mesh = meshDef.isSkinnedMesh === true - ? new SkinnedMesh( geometry, material ) - : new Mesh( geometry, material ); + } - if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { + function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) { - // we normalize floating point skin weight array to fix malformed assets (see #15319) - // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs - mesh.normalizeSkinWeights(); + const numComponents = attribute.num_components(); + const numPoints = dracoGeometry.num_points(); + const numValues = numPoints * numComponents; + const byteLength = numValues * attributeType.BYTES_PER_ELEMENT; + const dataType = getDracoDataType( draco, attributeType ); - } + const ptr = draco._malloc( byteLength ); + decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr ); + const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice(); + draco._free( ptr ); - if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + return { + name: attributeName, + array: array, + itemSize: numComponents + }; - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); + } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + function getDracoDataType( draco, attributeType ) { - mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); + switch ( attributeType ) { - } + case Float32Array: return draco.DT_FLOAT32; + case Int8Array: return draco.DT_INT8; + case Int16Array: return draco.DT_INT16; + case Int32Array: return draco.DT_INT32; + case Uint8Array: return draco.DT_UINT8; + case Uint16Array: return draco.DT_UINT16; + case Uint32Array: return draco.DT_UINT32; - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + } - mesh = new LineSegments( geometry, material ); + } - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { +} - mesh = new Line( geometry, material ); +class GLTFLoader extends Loader { - } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + constructor( manager ) { - mesh = new LineLoop( geometry, material ); + super( manager ); - } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + this.dracoLoader = null; + this.ktx2Loader = null; + this.meshoptDecoder = null; - mesh = new Points( geometry, material ); + this.pluginCallbacks = []; - } else { + this.register( function ( parser ) { - throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); + return new GLTFMaterialsClearcoatExtension( parser ); - } + } ); - if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { + this.register( function ( parser ) { - updateMorphTargets( mesh, meshDef ); + return new GLTFTextureBasisUExtension( parser ); - } + } ); - mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); + this.register( function ( parser ) { - assignExtrasToUserData( mesh, meshDef ); + return new GLTFTextureWebPExtension( parser ); - if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); + } ); - parser.assignFinalMaterial( mesh ); + this.register( function ( parser ) { - meshes.push( mesh ); + return new GLTFMaterialsSheenExtension( parser ); - } + } ); - if ( meshes.length === 1 ) { + this.register( function ( parser ) { - return meshes[ 0 ]; + return new GLTFMaterialsTransmissionExtension( parser ); - } + } ); - const group = new Group(); + this.register( function ( parser ) { - for ( let i = 0, il = meshes.length; i < il; i ++ ) { + return new GLTFMaterialsVolumeExtension( parser ); - group.add( meshes[ i ] ); + } ); - } + this.register( function ( parser ) { - return group; + return new GLTFMaterialsIorExtension( parser ); } ); - } - - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras - * @param {number} cameraIndex - * @return {Promise} - */ - loadCamera( cameraIndex ) { + this.register( function ( parser ) { - let camera; - const cameraDef = this.json.cameras[ cameraIndex ]; - const params = cameraDef[ cameraDef.type ]; + return new GLTFMaterialsSpecularExtension( parser ); - if ( ! params ) { + } ); - console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); - return; + this.register( function ( parser ) { - } + return new GLTFLightsExtension( parser ); - if ( cameraDef.type === 'perspective' ) { + } ); - camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); + this.register( function ( parser ) { - } else if ( cameraDef.type === 'orthographic' ) { + return new GLTFMeshoptCompression( parser ); - camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); + } ); - } + } - if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); + load( url, onLoad, onProgress, onError ) { - assignExtrasToUserData( camera, cameraDef ); + const scope = this; - return Promise.resolve( camera ); + let resourcePath; - } + if ( this.resourcePath !== '' ) { - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins - * @param {number} skinIndex - * @return {Promise} - */ - loadSkin( skinIndex ) { + resourcePath = this.resourcePath; - const skinDef = this.json.skins[ skinIndex ]; + } else if ( this.path !== '' ) { - const skinEntry = { joints: skinDef.joints }; + resourcePath = this.path; - if ( skinDef.inverseBindMatrices === undefined ) { + } else { - return Promise.resolve( skinEntry ); + resourcePath = LoaderUtils.extractUrlBase( url ); } - return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { + // Tells the LoadingManager to track an extra item, which resolves after + // the model is fully loaded. This means the count of items loaded will + // be incorrect, but ensures manager.onLoad() does not fire early. + this.manager.itemStart( url ); - skinEntry.inverseBindMatrices = accessor; + const _onError = function ( e ) { - return skinEntry; + if ( onError ) { - } ); + onError( e ); - } + } else { - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations - * @param {number} animationIndex - * @return {Promise} - */ - loadAnimation( animationIndex ) { + console.error( e ); - const json = this.json; + } - const animationDef = json.animations[ animationIndex ]; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - const pendingNodes = []; - const pendingInputAccessors = []; - const pendingOutputAccessors = []; - const pendingSamplers = []; - const pendingTargets = []; + }; - for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { + const loader = new FileLoader( this.manager ); - const channel = animationDef.channels[ i ]; - const sampler = animationDef.samplers[ channel.sampler ]; - const target = channel.target; - const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. - const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; - const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); - pendingNodes.push( this.getDependency( 'node', name ) ); - pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); - pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); - pendingSamplers.push( sampler ); - pendingTargets.push( target ); + loader.load( url, function ( data ) { - } + try { - return Promise.all( [ + scope.parse( data, resourcePath, function ( gltf ) { - Promise.all( pendingNodes ), - Promise.all( pendingInputAccessors ), - Promise.all( pendingOutputAccessors ), - Promise.all( pendingSamplers ), - Promise.all( pendingTargets ) + onLoad( gltf ); - ] ).then( function ( dependencies ) { + scope.manager.itemEnd( url ); - const nodes = dependencies[ 0 ]; - const inputAccessors = dependencies[ 1 ]; - const outputAccessors = dependencies[ 2 ]; - const samplers = dependencies[ 3 ]; - const targets = dependencies[ 4 ]; + }, _onError ); - const tracks = []; + } catch ( e ) { - for ( let i = 0, il = nodes.length; i < il; i ++ ) { + _onError( e ); - const node = nodes[ i ]; - const inputAccessor = inputAccessors[ i ]; - const outputAccessor = outputAccessors[ i ]; - const sampler = samplers[ i ]; - const target = targets[ i ]; + } - if ( node === undefined ) continue; + }, onProgress, _onError ); - node.updateMatrix(); - node.matrixAutoUpdate = true; + } - let TypedKeyframeTrack; + setDRACOLoader( dracoLoader ) { - switch ( PATH_PROPERTIES$1[ target.path ] ) { + this.dracoLoader = dracoLoader; + return this; - case PATH_PROPERTIES$1.weights: + } - TypedKeyframeTrack = NumberKeyframeTrack; - break; + setDDSLoader() { - case PATH_PROPERTIES$1.rotation: + throw new Error( - TypedKeyframeTrack = QuaternionKeyframeTrack; - break; + 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' - case PATH_PROPERTIES$1.position: - case PATH_PROPERTIES$1.scale: - default: + ); - TypedKeyframeTrack = VectorKeyframeTrack; - break; + } - } + setKTX2Loader( ktx2Loader ) { - const targetName = node.name ? node.name : node.uuid; + this.ktx2Loader = ktx2Loader; + return this; - const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : InterpolateLinear; + } - const targetNames = []; + setMeshoptDecoder( meshoptDecoder ) { - if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + this.meshoptDecoder = meshoptDecoder; + return this; - // Node may be a Group (glTF mesh with several primitives) or a Mesh. - node.traverse( function ( object ) { + } - if ( object.isMesh === true && object.morphTargetInfluences ) { + register( callback ) { - targetNames.push( object.name ? object.name : object.uuid ); + if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) { - } + this.pluginCallbacks.push( callback ); - } ); + } - } else { + return this; - targetNames.push( targetName ); + } - } + unregister( callback ) { - let outputArray = outputAccessor.array; + if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) { - if ( outputAccessor.normalized ) { + this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 ); - const scale = getNormalizedComponentScale( outputArray.constructor ); - const scaled = new Float32Array( outputArray.length ); + } - for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { + return this; - scaled[ j ] = outputArray[ j ] * scale; + } - } + parse( data, path, onLoad, onError ) { - outputArray = scaled; + let content; + const extensions = {}; + const plugins = {}; - } + if ( typeof data === 'string' ) { - for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { + content = data; - const track = new TypedKeyframeTrack( - targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], - inputAccessor.array, - outputArray, - interpolation - ); + } else { - // Override interpolation with custom factory method. - if ( sampler.interpolation === 'CUBICSPLINE' ) { + const magic = LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) ); - track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { + if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) { - // A CUBICSPLINE keyframe in glTF has three output values for each input value, - // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() - // must be divided by three to get the interpolant's sampleSize argument. + try { - return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result ); + extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ] = new GLTFBinaryExtension$1( data ); - }; + } catch ( error ) { - // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. - track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; + if ( onError ) onError( error ); + return; - } + } - tracks.push( track ); + content = extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].content; - } + } else { + + content = LoaderUtils.decodeText( new Uint8Array( data ) ); } - const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; + } - return new AnimationClip( name, undefined, tracks ); + const json = JSON.parse( content ); + + if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) { + + if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) ); + return; + + } + + const parser = new GLTFParser$1( json, { + + path: path || this.resourcePath || '', + crossOrigin: this.crossOrigin, + requestHeader: this.requestHeader, + manager: this.manager, + ktx2Loader: this.ktx2Loader, + meshoptDecoder: this.meshoptDecoder } ); - } + parser.fileLoader.setRequestHeader( this.requestHeader ); - createNodeMesh( nodeIndex ) { + for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) { - const json = this.json; - const parser = this; - const nodeDef = json.nodes[ nodeIndex ]; + const plugin = this.pluginCallbacks[ i ]( parser ); + plugins[ plugin.name ] = plugin; - if ( nodeDef.mesh === undefined ) return null; + // Workaround to avoid determining as unknown extension + // in addUnknownExtensionsToUserData(). + // Remove this workaround if we move all the existing + // extension handlers to plugin system + extensions[ plugin.name ] = true; - return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { + } - const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); + if ( json.extensionsUsed ) { - // if weights are provided on the node, override weights on the mesh. - if ( nodeDef.weights !== undefined ) { + for ( let i = 0; i < json.extensionsUsed.length; ++ i ) { - node.traverse( function ( o ) { + const extensionName = json.extensionsUsed[ i ]; + const extensionsRequired = json.extensionsRequired || []; - if ( ! o.isMesh ) return; + switch ( extensionName ) { - for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { + case EXTENSIONS$1.KHR_MATERIALS_UNLIT: + extensions[ extensionName ] = new GLTFMaterialsUnlitExtension(); + break; - o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; + case EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: + extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension(); + break; - } + case EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION: + extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader ); + break; - } ); + case EXTENSIONS$1.KHR_TEXTURE_TRANSFORM: + extensions[ extensionName ] = new GLTFTextureTransformExtension(); + break; - } + case EXTENSIONS$1.KHR_MESH_QUANTIZATION: + extensions[ extensionName ] = new GLTFMeshQuantizationExtension(); + break; - return node; + default: - } ); + if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) { - } + console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' ); - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy - * @param {number} nodeIndex - * @return {Promise} - */ - loadNode( nodeIndex ) { + } - const json = this.json; - const extensions = this.extensions; - const parser = this; + } - const nodeDef = json.nodes[ nodeIndex ]; + } - // reserve node's name before its dependencies, so the root has the intended name. - const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; + } - return ( function () { + parser.setExtensions( extensions ); + parser.setPlugins( plugins ); + parser.parse( onLoad, onError ); - const pending = []; + } - const meshPromise = parser._invokeOne( function ( ext ) { + parseAsync( data, path ) { - return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); + const scope = this; - } ); + return new Promise( function ( resolve, reject ) { - if ( meshPromise ) { + scope.parse( data, path, resolve, reject ); - pending.push( meshPromise ); + } ); - } + } - if ( nodeDef.camera !== undefined ) { +} - pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { +/* GLTFREGISTRY */ - return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); +function GLTFRegistry$1() { - } ) ); + let objects = {}; - } + return { - parser._invokeAll( function ( ext ) { + get: function ( key ) { - return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); + return objects[ key ]; - } ).forEach( function ( promise ) { + }, - pending.push( promise ); + add: function ( key, object ) { - } ); + objects[ key ] = object; - return Promise.all( pending ); + }, - }() ).then( function ( objects ) { + remove: function ( key ) { - let node; + delete objects[ key ]; - // .isBone isn't in glTF spec. See ._markDefs - if ( nodeDef.isBone === true ) { + }, - node = new Bone(); + removeAll: function () { - } else if ( objects.length > 1 ) { + objects = {}; - node = new Group(); + } - } else if ( objects.length === 1 ) { + }; - node = objects[ 0 ]; +} - } else { +/*********************************/ +/********** EXTENSIONS ***********/ +/*********************************/ - node = new Object3D(); +const EXTENSIONS$1 = { + KHR_BINARY_GLTF: 'KHR_binary_glTF', + KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression', + KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual', + KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat', + KHR_MATERIALS_IOR: 'KHR_materials_ior', + KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness', + KHR_MATERIALS_SHEEN: 'KHR_materials_sheen', + KHR_MATERIALS_SPECULAR: 'KHR_materials_specular', + KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission', + KHR_MATERIALS_UNLIT: 'KHR_materials_unlit', + KHR_MATERIALS_VOLUME: 'KHR_materials_volume', + KHR_TEXTURE_BASISU: 'KHR_texture_basisu', + KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform', + KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization', + EXT_TEXTURE_WEBP: 'EXT_texture_webp', + EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression' +}; - } +/** + * Punctual Lights Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual + */ +class GLTFLightsExtension { - if ( node !== objects[ 0 ] ) { + constructor( parser ) { - for ( let i = 0, il = objects.length; i < il; i ++ ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_LIGHTS_PUNCTUAL; - node.add( objects[ i ] ); + // Object3D instance caches + this.cache = { refs: {}, uses: {} }; - } + } - } + _markDefs() { - if ( nodeDef.name ) { + const parser = this.parser; + const nodeDefs = this.parser.json.nodes || []; - node.userData.name = nodeDef.name; - node.name = nodeName; + for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { - } + const nodeDef = nodeDefs[ nodeIndex ]; - assignExtrasToUserData( node, nodeDef ); + if ( nodeDef.extensions + && nodeDef.extensions[ this.name ] + && nodeDef.extensions[ this.name ].light !== undefined ) { - if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); + parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light ); - if ( nodeDef.matrix !== undefined ) { + } - const matrix = new Matrix4(); - matrix.fromArray( nodeDef.matrix ); - node.applyMatrix4( matrix ); + } - } else { + } - if ( nodeDef.translation !== undefined ) { + _loadLight( lightIndex ) { - node.position.fromArray( nodeDef.translation ); + const parser = this.parser; + const cacheKey = 'light:' + lightIndex; + let dependency = parser.cache.get( cacheKey ); - } + if ( dependency ) return dependency; - if ( nodeDef.rotation !== undefined ) { + const json = parser.json; + const extensions = ( json.extensions && json.extensions[ this.name ] ) || {}; + const lightDefs = extensions.lights || []; + const lightDef = lightDefs[ lightIndex ]; + let lightNode; - node.quaternion.fromArray( nodeDef.rotation ); + const color = new Color( 0xffffff ); - } + if ( lightDef.color !== undefined ) color.fromArray( lightDef.color ); - if ( nodeDef.scale !== undefined ) { + const range = lightDef.range !== undefined ? lightDef.range : 0; - node.scale.fromArray( nodeDef.scale ); + switch ( lightDef.type ) { - } + case 'directional': + lightNode = new DirectionalLight( color ); + lightNode.target.position.set( 0, 0, - 1 ); + lightNode.add( lightNode.target ); + break; - } + case 'point': + lightNode = new PointLight( color ); + lightNode.distance = range; + break; - parser.associations.set( node, { type: 'nodes', index: nodeIndex } ); + case 'spot': + lightNode = new SpotLight( color ); + lightNode.distance = range; + // Handle spotlight properties. + lightDef.spot = lightDef.spot || {}; + lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0; + lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0; + lightNode.angle = lightDef.spot.outerConeAngle; + lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle; + lightNode.target.position.set( 0, 0, - 1 ); + lightNode.add( lightNode.target ); + break; - return node; + default: + throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type ); - } ); + } - } + // Some lights (e.g. spot) default to a position other than the origin. Reset the position + // here, because node-level parsing will only override position if explicitly specified. + lightNode.position.set( 0, 0, 0 ); - /** - * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes - * @param {number} sceneIndex - * @return {Promise} - */ - loadScene( sceneIndex ) { + lightNode.decay = 2; - const json = this.json; - const extensions = this.extensions; - const sceneDef = this.json.scenes[ sceneIndex ]; - const parser = this; + if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity; - // Loader returns Group, not Scene. - // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 - const scene = new Group(); - if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); + lightNode.name = parser.createUniqueName( lightDef.name || ( 'light_' + lightIndex ) ); - assignExtrasToUserData( scene, sceneDef ); + dependency = Promise.resolve( lightNode ); - if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); + parser.cache.add( cacheKey, dependency ); - const nodeIds = sceneDef.nodes || []; + return dependency; - const pending = []; + } - for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { + createNodeAttachment( nodeIndex ) { - pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) ); + const self = this; + const parser = this.parser; + const json = parser.json; + const nodeDef = json.nodes[ nodeIndex ]; + const lightDef = ( nodeDef.extensions && nodeDef.extensions[ this.name ] ) || {}; + const lightIndex = lightDef.light; - } + if ( lightIndex === undefined ) return null; - return Promise.all( pending ).then( function () { + return this._loadLight( lightIndex ).then( function ( light ) { - return scene; + return parser._getNodeRef( self.cache, lightIndex, light ); } ); @@ -46412,1076 +45944,4555 @@ class GLTFParser$1 { } -function buildNodeHierachy( nodeId, parentObject, json, parser ) { +/** + * Unlit Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit + */ +class GLTFMaterialsUnlitExtension { - const nodeDef = json.nodes[ nodeId ]; + constructor() { - return parser.getDependency( 'node', nodeId ).then( function ( node ) { + this.name = EXTENSIONS$1.KHR_MATERIALS_UNLIT; - if ( nodeDef.skin === undefined ) return node; + } - // build skeleton here as well + getMaterialType() { - let skinEntry; + return MeshBasicMaterial; - return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { + } - skinEntry = skin; + extendParams( materialParams, materialDef, parser ) { - const pendingJoints = []; + const pending = []; - for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; - pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); + const metallicRoughness = materialDef.pbrMetallicRoughness; - } + if ( metallicRoughness ) { - return Promise.all( pendingJoints ); + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { - } ).then( function ( jointNodes ) { + const array = metallicRoughness.baseColorFactor; - node.traverse( function ( mesh ) { + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; - if ( ! mesh.isMesh ) return; + } - const bones = []; - const boneInverses = []; + if ( metallicRoughness.baseColorTexture !== undefined ) { - for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { + pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) ); - const jointNode = jointNodes[ j ]; + } - if ( jointNode ) { + } - bones.push( jointNode ); + return Promise.all( pending ); - const mat = new Matrix4(); + } - if ( skinEntry.inverseBindMatrices !== undefined ) { +} - mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); +/** + * Clearcoat Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ +class GLTFMaterialsClearcoatExtension { - } + constructor( parser ) { - boneInverses.push( mat ); + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_CLEARCOAT; - } else { + } - console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); + getMaterialType( materialIndex ) { - } + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); + return MeshPhysicalMaterial; - } ); + } - return node; + extendMaterialParams( materialIndex, materialParams ) { - } ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } ).then( function ( node ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - // build node hierachy + return Promise.resolve(); - parentObject.add( node ); + } const pending = []; - if ( nodeDef.children ) { + const extension = materialDef.extensions[ this.name ]; - const children = nodeDef.children; + if ( extension.clearcoatFactor !== undefined ) { - for ( let i = 0, il = children.length; i < il; i ++ ) { + materialParams.clearcoat = extension.clearcoatFactor; - const child = children[ i ]; - pending.push( buildNodeHierachy( child, node, json, parser ) ); + } - } + if ( extension.clearcoatTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) ); } - return Promise.all( pending ); + if ( extension.clearcoatRoughnessFactor !== undefined ) { - } ); + materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor; -} + } -/** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - */ -function computeBounds( geometry, primitiveDef, parser ) { + if ( extension.clearcoatRoughnessTexture !== undefined ) { - const attributes = primitiveDef.attributes; + pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) ); - const box = new Box3(); + } - if ( attributes.POSITION !== undefined ) { + if ( extension.clearcoatNormalTexture !== undefined ) { - const accessor = parser.json.accessors[ attributes.POSITION ]; + pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) ); - const min = accessor.min; - const max = accessor.max; + if ( extension.clearcoatNormalTexture.scale !== undefined ) { - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + const scale = extension.clearcoatNormalTexture.scale; - if ( min !== undefined && max !== undefined ) { + materialParams.clearcoatNormalScale = new Vector2( scale, scale ); - box.set( - new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), - new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) - ); + } - if ( accessor.normalized ) { + } - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); - box.min.multiplyScalar( boxScale ); - box.max.multiplyScalar( boxScale ); + return Promise.all( pending ); - } + } - } else { +} - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); +/** + * Sheen Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen + */ +class GLTFMaterialsSheenExtension { - return; + constructor( parser ) { - } + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_SHEEN; - } else { + } - return; + getMaterialType( materialIndex ) { - } + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - const targets = primitiveDef.targets; + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - if ( targets !== undefined ) { + return MeshPhysicalMaterial; - const maxDisplacement = new Vector3(); - const vector = new Vector3(); + } - for ( let i = 0, il = targets.length; i < il; i ++ ) { + extendMaterialParams( materialIndex, materialParams ) { - const target = targets[ i ]; + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - if ( target.POSITION !== undefined ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - const accessor = parser.json.accessors[ target.POSITION ]; - const min = accessor.min; - const max = accessor.max; + return Promise.resolve(); - // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + } - if ( min !== undefined && max !== undefined ) { + const pending = []; - // we need to get max of absolute components because target weight is [-1,1] - vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); - vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); - vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); + materialParams.sheenColor = new Color( 0, 0, 0 ); + materialParams.sheenRoughness = 0; + materialParams.sheen = 1; + const extension = materialDef.extensions[ this.name ]; - if ( accessor.normalized ) { + if ( extension.sheenColorFactor !== undefined ) { - const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); - vector.multiplyScalar( boxScale ); + materialParams.sheenColor.fromArray( extension.sheenColorFactor ); - } + } - // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative - // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets - // are used to implement key-frame animations and as such only two are active at a time - this results in very large - // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. - maxDisplacement.max( vector ); + if ( extension.sheenRoughnessFactor !== undefined ) { - } else { + materialParams.sheenRoughness = extension.sheenRoughnessFactor; - console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + } - } + if ( extension.sheenColorTexture !== undefined ) { - } + pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, sRGBEncoding ) ); } - // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. - box.expandByVector( maxDisplacement ); - - } + if ( extension.sheenRoughnessTexture !== undefined ) { - geometry.boundingBox = box; + pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) ); - const sphere = new Sphere(); + } - box.getCenter( sphere.center ); - sphere.radius = box.min.distanceTo( box.max ) / 2; + return Promise.all( pending ); - geometry.boundingSphere = sphere; + } } /** - * @param {BufferGeometry} geometry - * @param {GLTF.Primitive} primitiveDef - * @param {GLTFParser} parser - * @return {Promise} + * Transmission Materials Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission + * Draft: https://github.com/KhronosGroup/glTF/pull/1698 */ -function addPrimitiveAttributes( geometry, primitiveDef, parser ) { +class GLTFMaterialsTransmissionExtension { - const attributes = primitiveDef.attributes; + constructor( parser ) { - const pending = []; + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_TRANSMISSION; - function assignAttributeAccessor( accessorIndex, attributeName ) { + } - return parser.getDependency( 'accessor', accessorIndex ) - .then( function ( accessor ) { + getMaterialType( materialIndex ) { - geometry.setAttribute( attributeName, accessor ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - } + return MeshPhysicalMaterial; - for ( const gltfAttributeName in attributes ) { + } - const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + extendMaterialParams( materialIndex, materialParams ) { - // Skip attributes already provided by e.g. Draco extension. - if ( threeAttributeName in geometry.attributes ) continue; + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - } + return Promise.resolve(); - if ( primitiveDef.indices !== undefined && ! geometry.index ) { + } - const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { + const pending = []; - geometry.setIndex( accessor ); + const extension = materialDef.extensions[ this.name ]; - } ); + if ( extension.transmissionFactor !== undefined ) { - pending.push( accessor ); + materialParams.transmission = extension.transmissionFactor; - } + } - assignExtrasToUserData( geometry, primitiveDef ); + if ( extension.transmissionTexture !== undefined ) { - computeBounds( geometry, primitiveDef, parser ); + pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) ); - return Promise.all( pending ).then( function () { + } - return primitiveDef.targets !== undefined - ? addMorphTargets( geometry, primitiveDef.targets, parser ) - : geometry; + return Promise.all( pending ); - } ); + } } /** - * @param {BufferGeometry} geometry - * @param {Number} drawMode - * @return {BufferGeometry} + * Materials Volume Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume */ -function toTrianglesDrawMode( geometry, drawMode ) { +class GLTFMaterialsVolumeExtension { - let index = geometry.getIndex(); + constructor( parser ) { - // generate index if not present + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_VOLUME; - if ( index === null ) { + } - const indices = []; + getMaterialType( materialIndex ) { - const position = geometry.getAttribute( 'position' ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - if ( position !== undefined ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - for ( let i = 0; i < position.count; i ++ ) { + return MeshPhysicalMaterial; - indices.push( i ); + } - } + extendMaterialParams( materialIndex, materialParams ) { - geometry.setIndex( indices ); - index = geometry.getIndex(); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } else { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); - return geometry; + return Promise.resolve(); } - } + const pending = []; - // + const extension = materialDef.extensions[ this.name ]; - const numberOfTriangles = index.count - 2; - const newIndices = []; + materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0; - if ( drawMode === TriangleFanDrawMode ) { + if ( extension.thicknessTexture !== undefined ) { - // gl.TRIANGLE_FAN + pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) ); - for ( let i = 1; i <= numberOfTriangles; i ++ ) { + } - newIndices.push( index.getX( 0 ) ); - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); + materialParams.attenuationDistance = extension.attenuationDistance || 0; - } + const colorArray = extension.attenuationColor || [ 1, 1, 1 ]; + materialParams.attenuationColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); - } else { + return Promise.all( pending ); - // gl.TRIANGLE_STRIP + } - for ( let i = 0; i < numberOfTriangles; i ++ ) { +} - if ( i % 2 === 0 ) { +/** + * Materials ior Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior + */ +class GLTFMaterialsIorExtension { - newIndices.push( index.getX( i ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i + 2 ) ); + constructor( parser ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_IOR; - } else { + } - newIndices.push( index.getX( i + 2 ) ); - newIndices.push( index.getX( i + 1 ) ); - newIndices.push( index.getX( i ) ); + getMaterialType( materialIndex ) { - } + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; + + return MeshPhysicalMaterial; } - if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { + extendMaterialParams( materialIndex, materialParams ) { - console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - } + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - // build final geometry + return Promise.resolve(); - const newGeometry = geometry.clone(); - newGeometry.setIndex( newIndices ); + } - return newGeometry; + const extension = materialDef.extensions[ this.name ]; + + materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5; + + return Promise.resolve(); + + } } -class VRButton { +/** + * Materials specular Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular + */ +class GLTFMaterialsSpecularExtension { - static createButton( renderer, options ) { + constructor( parser ) { - if ( options ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_MATERIALS_SPECULAR; - console.error( 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.' ); + } - } + getMaterialType( materialIndex ) { - const button = document.createElement( 'button' ); + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - function showEnterVR( /*device*/ ) { + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null; - let currentSession = null; + return MeshPhysicalMaterial; - async function onSessionStarted( session ) { + } - session.addEventListener( 'end', onSessionEnded ); + extendMaterialParams( materialIndex, materialParams ) { - await renderer.xr.setSession( session ); - button.textContent = 'EXIT VR'; + const parser = this.parser; + const materialDef = parser.json.materials[ materialIndex ]; - currentSession = session; + if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) { - } + return Promise.resolve(); - function onSessionEnded( /*event*/ ) { + } - currentSession.removeEventListener( 'end', onSessionEnded ); + const pending = []; - button.textContent = 'ENTER VR'; + const extension = materialDef.extensions[ this.name ]; - currentSession = null; + materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0; - } + if ( extension.specularTexture !== undefined ) { - // + pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) ); - button.style.display = ''; + } - button.style.cursor = 'pointer'; - button.style.left = 'calc(50% - 50px)'; - button.style.width = '100px'; + const colorArray = extension.specularColorFactor || [ 1, 1, 1 ]; + materialParams.specularColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] ); - button.textContent = 'ENTER VR'; + if ( extension.specularColorTexture !== undefined ) { - button.onmouseenter = function () { + pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, sRGBEncoding ) ); - button.style.opacity = '1.0'; + } - }; + return Promise.all( pending ); - button.onmouseleave = function () { + } - button.style.opacity = '0.5'; +} - }; +/** + * BasisU Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu + */ +class GLTFTextureBasisUExtension { - button.onclick = function () { + constructor( parser ) { - if ( currentSession === null ) { + this.parser = parser; + this.name = EXTENSIONS$1.KHR_TEXTURE_BASISU; - // WebXR's requestReferenceSpace only works if the corresponding feature - // was requested at session creation time. For simplicity, just ask for - // the interesting ones as optional features, but be aware that the - // requestReferenceSpace call will fail if it turns out to be unavailable. - // ('local' is always available for immersive sessions and doesn't need to - // be requested separately.) + } - const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking' ] }; - navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted ); + loadTexture( textureIndex ) { - } else { + const parser = this.parser; + const json = parser.json; - currentSession.end(); + const textureDef = json.textures[ textureIndex ]; - } + if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) { - }; + return null; } - function disableButton() { + const extension = textureDef.extensions[ this.name ]; + const loader = parser.options.ktx2Loader; - button.style.display = ''; + if ( ! loader ) { - button.style.cursor = 'auto'; - button.style.left = 'calc(50% - 75px)'; - button.style.width = '150px'; + if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - button.onmouseenter = null; - button.onmouseleave = null; + throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' ); - button.onclick = null; + } else { + + // Assumes that the extension is optional and that a fallback texture is present + return null; + + } } - function showWebXRNotFound() { + return parser.loadTextureImage( textureIndex, extension.source, loader ); - disableButton(); + } - button.textContent = 'VR NOT SUPPORTED'; +} + +/** + * WebP Texture Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp + */ +class GLTFTextureWebPExtension { + + constructor( parser ) { + + this.parser = parser; + this.name = EXTENSIONS$1.EXT_TEXTURE_WEBP; + this.isSupported = null; + + } + + loadTexture( textureIndex ) { + + const name = this.name; + const parser = this.parser; + const json = parser.json; + + const textureDef = json.textures[ textureIndex ]; + + if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) { + + return null; } - function stylizeElement( element ) { + const extension = textureDef.extensions[ name ]; + const source = json.images[ extension.source ]; - element.style.position = 'absolute'; - element.style.bottom = '20px'; - element.style.padding = '12px 6px'; - element.style.border = '1px solid #fff'; - element.style.borderRadius = '4px'; - element.style.background = 'rgba(0,0,0,0.1)'; - element.style.color = '#fff'; - element.style.font = 'normal 13px sans-serif'; - element.style.textAlign = 'center'; - element.style.opacity = '0.5'; - element.style.outline = 'none'; - element.style.zIndex = '999'; + let loader = parser.textureLoader; + if ( source.uri ) { + + const handler = parser.options.manager.getHandler( source.uri ); + if ( handler !== null ) loader = handler; } - if ( 'xr' in navigator ) { + return this.detectSupport().then( function ( isSupported ) { - button.id = 'VRButton'; - button.style.display = 'none'; + if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader ); - stylizeElement( button ); + if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) { - navigator.xr.isSessionSupported( 'immersive-vr' ).then( function ( supported ) { + throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' ); - supported ? showEnterVR() : showWebXRNotFound(); + } - } ); + // Fall back to PNG or JPEG. + return parser.loadTexture( textureIndex ); - return button; + } ); - } else { + } - const message = document.createElement( 'a' ); + detectSupport() { - if ( window.isSecureContext === false ) { + if ( ! this.isSupported ) { - message.href = document.location.href.replace( /^http:/, 'https:' ); - message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message + this.isSupported = new Promise( function ( resolve ) { - } else { + const image = new Image(); - message.href = 'https://immersiveweb.dev/'; - message.innerHTML = 'WEBXR NOT AVAILABLE'; + // Lossy test image. Support for lossy images doesn't guarantee support for all + // WebP images, unfortunately. + image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'; - } + image.onload = image.onerror = function () { - message.style.left = 'calc(50% - 90px)'; - message.style.width = '180px'; - message.style.textDecoration = 'none'; + resolve( image.height === 1 ); - stylizeElement( message ); + }; - return message; + } ); } + return this.isSupported; + } } -/*! - * three-icosa - * https://github.com/icosa-gallery/three-icosa - * Copyright (c) 2021-2022 Icosa Gallery - * Released under the Apache 2.0 Licence. +/** + * meshopt BufferView Compression Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression */ +class GLTFMeshoptCompression { -// Copyright 2021-2022 Icosa Gallery + constructor( parser ) { -class TiltShaderLoader extends Loader { - constructor( manager ) { - super( manager ); - - this.loadedMaterials = {}; - } - - async load(brushName, onLoad, onProgress, onError ) { - const scope = this; + this.name = EXTENSIONS$1.EXT_MESHOPT_COMPRESSION; + this.parser = parser; - const isAlreadyLoaded = this.loadedMaterials[brushName]; - - if (isAlreadyLoaded !== undefined) { - onLoad( scope.parse( isAlreadyLoaded ) ); - return; - } - - const loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'text' ); - loader.setWithCredentials( this.withCredentials ); + } - const textureLoader = new TextureLoader(this.manager); - textureLoader.setPath(this.path); - textureLoader.setWithCredentials( this.withCredentials ); + loadBufferView( index ) { - const materialParams = tiltBrushMaterialParams[brushName]; + const json = this.parser.json; + const bufferView = json.bufferViews[ index ]; - materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); - materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + if ( bufferView.extensions && bufferView.extensions[ this.name ] ) { - if (materialParams.uniforms.u_MainTex) { - const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); - mainTex.name = `${brushName}_MainTex`; - mainTex.wrapS = RepeatWrapping; - mainTex.wrapT = RepeatWrapping; - mainTex.flipY = false; - materialParams.uniforms.u_MainTex.value = mainTex; - } + const extensionDef = bufferView.extensions[ this.name ]; - if (materialParams.uniforms.u_BumpMap) { - const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); - bumpMap.name = `${brushName}_BumpMap`; - bumpMap.wrapS = RepeatWrapping; - bumpMap.wrapT = RepeatWrapping; - bumpMap.flipY = false; - materialParams.uniforms.u_BumpMap.value = bumpMap; - } + const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer ); + const decoder = this.parser.options.meshoptDecoder; - if (materialParams.uniforms.u_AlphaMask) { - const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); - alphaMask.name = `${brushName}_AlphaMask`; - alphaMask.wrapS = RepeatWrapping; - alphaMask.wrapT = RepeatWrapping; - alphaMask.flipY = false; - materialParams.uniforms.u_AlphaMask.value = alphaMask; - } + if ( ! decoder || ! decoder.supported ) { - // inject three.js lighting uniforms - for(var lightType in UniformsLib.lights) - { - materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; - } + if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) { - let rawMaterial = new RawShaderMaterial(materialParams); - this.loadedMaterials[brushName] = rawMaterial; - onLoad( scope.parse( rawMaterial ) ); - } + throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' ); - parse( rawMaterial ) { - return rawMaterial; - } + } else { - lookupMaterial(nameOrGuid) { - const name = this.lookupMaterialName(nameOrGuid); - return tiltBrushMaterialParams[name]; - } + // Assumes that the extension is optional and that fallback buffer data is present + return null; - lookupMaterialName(nameOrGuid) { - switch(nameOrGuid) { - case "BlocksBasic:": - case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": - return "BlocksBasic"; + } - case "BlocksGem": - case "232998f8-d357-47a2-993a-53415df9be10": - return "BlocksGem"; + } - case "BlocksGlass": - case "3d813d82-5839-4450-8ddc-8e889ecd96c7": - return "BlocksGlass"; + return Promise.all( [ buffer, decoder.ready ] ).then( function ( res ) { - case "Bubbles": - case "89d104cd-d012-426b-b5b3-bbaee63ac43c": - return "Bubbles"; + const byteOffset = extensionDef.byteOffset || 0; + const byteLength = extensionDef.byteLength || 0; - case "CelVinyl": - case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": - return "CelVinyl"; + const count = extensionDef.count; + const stride = extensionDef.byteStride; - case "ChromaticWave": - case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": - return "ChromaticWave"; + const result = new ArrayBuffer( count * stride ); + const source = new Uint8Array( res[ 0 ], byteOffset, byteLength ); - case "CoarseBristles": - case "1161af82-50cf-47db-9706-0c3576d43c43": - case "79168f10-6961-464a-8be1-57ed364c5600": - return "CoarseBristles"; - - case "Comet": - case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": - return "Comet"; - - case "DiamondHull": - case "c8313697-2563-47fc-832e-290f4c04b901": - return "DiamondHull"; - - case "Disco": - case "4391aaaa-df73-4396-9e33-31e4e4930b27": - return "Disco"; - - case "DotMarker": - case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": - return "DotMarker"; - - case "Dots": - case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": - return "Dots"; + decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter ); + return result; - case "DoubleTaperedFlat": - case "0d3889f3-3ede-470c-8af4-f44813306126": - return "DoubleTaperedFlat"; - - case "DoubleTaperedMarker": - case "0d3889f3-3ede-470c-8af4-de4813306126": - return "DoubleTaperedMarker"; - - case "DuctTape": - case "d0262945-853c-4481-9cbd-88586bed93cb": - case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": - return "DuctTape"; - - case "Electricity": - case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": - return "Electricity"; + } ); - case "Embers": - case "02ffb866-7fb2-4d15-b761-1012cefb1360": - return "Embers"; - - case "EnvironmentDiffuse": - case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": - return "EnvironmentDiffuse"; - - case "EnvironmentDiffuseLightMap": - case "d01d9d6c-9a61-4aba-8146-5891fafb013b": - return "EnvironmentDiffuseLightMap"; + } else { - case "Fire": - case "cb92b597-94ca-4255-b017-0e3f42f12f9e": - return "Fire"; + return null; - case "2d35bcf0-e4d8-452c-97b1-3311be063130": - case "280c0a7a-aad8-416c-a7d2-df63d129ca70": - case "55303bc4-c749-4a72-98d9-d23e68e76e18": - case "Flat": - return "Flat"; - - case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": - case "geometry_Highlighter": - return "Highlighter"; - - case "Hypercolor": - case "dce872c2-7b49-4684-b59b-c45387949c5c": - case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": - return "Hypercolor"; - - case "HyperGrid": - case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": - return "HyperGrid"; + } - case "Icing": - case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": - return "Icing"; - - case "Ink": - case "f5c336cf-5108-4b40-ade9-c687504385ab": - case "c0012095-3ffd-4040-8ee1-fc180d346eaa": - return "Ink"; + } - case "Leaves": - case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": - case "ea19de07-d0c0-4484-9198-18489a3c1487": - return "Leaves"; +} - case "Light": - case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": - return "Light"; +/* BINARY EXTENSION */ +const BINARY_EXTENSION_HEADER_MAGIC = 'glTF'; +const BINARY_EXTENSION_HEADER_LENGTH$1 = 12; +const BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; - case "LightWire": - case "4391aaaa-df81-4396-9e33-31e4e4930b27": - return "LightWire"; - - case "Lofted": - case "d381e0f5-3def-4a0d-8853-31e9200bcbda": - return "Lofted"; +class GLTFBinaryExtension$1 { - case "Marker": - case "429ed64a-4e97-4466-84d3-145a861ef684": - return "Marker"; - - case "MatteHull": - case "79348357-432d-4746-8e29-0e25c112e3aa": - return "MatteHull"; + constructor( data ) { - case "NeonPulse": - case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": - return "NeonPulse"; + this.name = EXTENSIONS$1.KHR_BINARY_GLTF; + this.content = null; + this.body = null; - case "OilPaint": - case "f72ec0e7-a844-4e38-82e3-140c44772699": - case "c515dad7-4393-4681-81ad-162ef052241b": - return "OilPaint"; + const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH$1 ); - case "Paper": - case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": - case "759f1ebd-20cd-4720-8d41-234e0da63716": - return "Paper"; - - case "PbrTemplate": - case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": - return "PbrTemplate"; - - case "PbrTransparentTemplate": - case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": - return "PbrTransparentTemplate"; - - case "Petal": - case "e0abbc80-0f80-e854-4970-8924a0863dcc": - return "Petal"; + this.header = { + magic: LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ), + version: headerView.getUint32( 4, true ), + length: headerView.getUint32( 8, true ) + }; - case "Plasma": - case "c33714d1-b2f9-412e-bd50-1884c9d46336": - return "Plasma"; - - case "Rainbow": - case "ad1ad437-76e2-450d-a23a-e17f8310b960": - return "Rainbow"; + if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) { - case "ShinyHull": - case "faaa4d44-fcfb-4177-96be-753ac0421ba3": - return "ShinyHull"; + throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' ); - case "Smoke": - case "70d79cca-b159-4f35-990c-f02193947fe8": - return "Smoke"; - - case "Snow": - case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": - return "Snow"; + } else if ( this.header.version < 2.0 ) { - case "SoftHighlighter": - case "accb32f5-4509-454f-93f8-1df3fd31df1b": - return "SoftHighlighter"; - - case "Spikes": - case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": - return "Spikes"; - - case "Splatter": - case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": - case "7a1c8107-50c5-4b70-9a39-421576d6617e": - return "Splatter"; - - case "Stars": - case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": - return "Stars"; + throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' ); - case "Streamers": - case "44bb800a-fbc3-4592-8426-94ecb05ddec3": - return "Streamers"; - - case "Taffy": - case "0077f88c-d93a-42f3-b59b-b31c50cdb414": - return "Taffy"; + } - case "TaperedFlat": - case "b468c1fb-f254-41ed-8ec9-57030bc5660c": - case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": - return "TaperedFlat"; + const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH$1; + const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH$1 ); + let chunkIndex = 0; - case "TaperedMarker": - case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": - case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": - return "TaperedMarker"; + while ( chunkIndex < chunkContentsLength ) { - case "ThickPaint": - case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": - case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": - return "ThickPaint"; - - case "Toon": - case "4391385a-df73-4396-9e33-31e4e4930b27": - return "Toon"; + const chunkLength = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; - case "UnlitHull": - case "a8fea537-da7c-4d4b-817f-24f074725d6d": - return "UnlitHull"; - - case "VelvetInk": - case "d229d335-c334-495a-a801-660ac8a87360": - return "VelvetInk"; + const chunkType = chunkView.getUint32( chunkIndex, true ); + chunkIndex += 4; - case "Waveform": - case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": - return "Waveform"; + if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) { - case "WetPaint": - case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": - case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": - return "WetPaint"; + const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex, chunkLength ); + this.content = LoaderUtils.decodeText( contentArray ); - case "WigglyGraphite": - case "5347acf0-a8e2-47b6-8346-30c70719d763": - case "e814fef1-97fd-7194-4a2f-50c2bb918be2": - return "WigglyGraphite"; + } else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) { + + const byteOffset = BINARY_EXTENSION_HEADER_LENGTH$1 + chunkIndex; + this.body = data.slice( byteOffset, byteOffset + chunkLength ); + + } + + // Clients must ignore chunks with unknown types. + + chunkIndex += chunkLength; + + } + + if ( this.content === null ) { + + throw new Error( 'THREE.GLTFLoader: JSON content not found.' ); + + } + + } - case "wire": - case "4391385a-cf83-4396-9e33-31e4e4930b27": - return "Wire"; - } } } -const tiltBrushMaterialParams = { - "BlocksBasic" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_Shininess: { value: 0.2 }, - u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 } - }, - vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", - fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, - depthTest: true, - blending: 0 - }, - "BlocksGem" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, - u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, - u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, - u_Color: { value: new Vector4(1, 1, 1, 1) }, - u_Shininess: { value: 0.9 }, - u_RimIntensity: { value: 0.5 }, - u_RimPower: { value: 2 }, - u_Frequency: { value: 2 }, - u_Jitter: { value: 1 }, - u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, - u_fogDensity: {value: 0 } - }, - vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", - fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, - depthTest: true, - blending: 0 - }, - "BlocksGlass" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_Color: { value: new Vector4(1, 1, 1, 1) }, - u_Shininess: { value: 0.8 }, - u_RimIntensity: { value: 0.7 }, - u_RimPower: { value: 4 }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 } - }, - vertexShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl", - fragmentShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: false, - depthTest: true, - blending: 2 - }, - "Bubbles" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_MainTex: { value: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png" }, - }, - vertexShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl", - fragmentShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl", - side: 2, - transparent: true, - depthFunc: 2, - depthWrite: false, - depthTest: true, - blending: 2 - }, - "CelVinyl" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_Cutoff: { value: 0.554 }, - u_MainTex: { value: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png" }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 }, - }, - vertexShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl", - fragmentShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, - depthTest: true, - blending: 0 - }, - "ChromaticWave" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_time: { value: new Vector4() }, - u_EmissionGain: { value: 0.45 }, - }, - vertexShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl", - fragmentShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl", - side: 2, - transparent: true, - depthFunc: 2, - depthWrite: false, - depthTest: true, - blending: 5, - blendDstAlpha: 201, - blendDst: 201, - blendEquationAlpha: 100, - blendEquation: 100, - blendSrcAlpha: 201, - blendSrc: 201 - }, - "CoarseBristles" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_MainTex: { value: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png" }, - u_Cutoff: { value: 0.25 }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 }, - }, - vertexShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl", - fragmentShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl", - side: 2, - transparent: false, - depthFunc: 2, - depthWrite: true, - depthTest: true, - blending: 0 - }, - "Comet" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_MainTex: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png" }, - u_AlphaMask: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png" }, - u_AlphaMask_TexelSize: { value: new Vector4(0.0156, 1, 64, 1)}, - u_time: { value: new Vector4() }, - u_Speed: { value: 1 }, - u_EmissionGain: { value: 0.5 }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 }, - }, - vertexShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl", - fragmentShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl", - side: 2, - transparent: true, - depthFunc: 2, - depthWrite: false, - depthTest: true, - blending: 2 - }, - "DiamondHull" : { - uniforms: { - u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, - u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, - u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, - u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, - u_MainTex: { value: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png" }, - u_time: { value: new Vector4() }, - cameraPosition: { value: new Vector3() }, - u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, - u_fogDensity: { value: 0 }, - }, - vertexShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl", - fragmentShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl", - side: 2, - transparent: true, - depthFunc: 2, - depthWrite: false, - depthTest: true, - blending: 5, - blendDstAlpha: 201, - blendDst: 201, - blendEquationAlpha: 100, - blendEquation: 100, +/** + * DRACO Mesh Compression Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression + */ +class GLTFDracoMeshCompressionExtension { + + constructor( json, dracoLoader ) { + + if ( ! dracoLoader ) { + + throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' ); + + } + + this.name = EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION; + this.json = json; + this.dracoLoader = dracoLoader; + this.dracoLoader.preload(); + + } + + decodePrimitive( primitive, parser ) { + + const json = this.json; + const dracoLoader = this.dracoLoader; + const bufferViewIndex = primitive.extensions[ this.name ].bufferView; + const gltfAttributeMap = primitive.extensions[ this.name ].attributes; + const threeAttributeMap = {}; + const attributeNormalizedMap = {}; + const attributeTypeMap = {}; + + for ( const attributeName in gltfAttributeMap ) { + + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); + + threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ]; + + } + + for ( const attributeName in primitive.attributes ) { + + const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase(); + + if ( gltfAttributeMap[ attributeName ] !== undefined ) { + + const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ]; + const componentType = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + + attributeTypeMap[ threeAttributeName ] = componentType; + attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true; + + } + + } + + return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) { + + return new Promise( function ( resolve ) { + + dracoLoader.decodeDracoFile( bufferView, function ( geometry ) { + + for ( const attributeName in geometry.attributes ) { + + const attribute = geometry.attributes[ attributeName ]; + const normalized = attributeNormalizedMap[ attributeName ]; + + if ( normalized !== undefined ) attribute.normalized = normalized; + + } + + resolve( geometry ); + + }, threeAttributeMap, attributeTypeMap ); + + } ); + + } ); + + } + +} + +/** + * Texture Transform Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform + */ +class GLTFTextureTransformExtension { + + constructor() { + + this.name = EXTENSIONS$1.KHR_TEXTURE_TRANSFORM; + + } + + extendTexture( texture, transform ) { + + if ( transform.texCoord !== undefined ) { + + console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' ); + + } + + if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) { + + // See https://github.com/mrdoob/three.js/issues/21819. + return texture; + + } + + texture = texture.clone(); + + if ( transform.offset !== undefined ) { + + texture.offset.fromArray( transform.offset ); + + } + + if ( transform.rotation !== undefined ) { + + texture.rotation = transform.rotation; + + } + + if ( transform.scale !== undefined ) { + + texture.repeat.fromArray( transform.scale ); + + } + + texture.needsUpdate = true; + + return texture; + + } + +} + +/** + * Specular-Glossiness Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness + */ + +/** + * A sub class of StandardMaterial with some of the functionality + * changed via the `onBeforeCompile` callback + * @pailhead + */ +class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { + + constructor( params ) { + + super(); + + this.isGLTFSpecularGlossinessMaterial = true; + + //various chunks that need replacing + const specularMapParsFragmentChunk = [ + '#ifdef USE_SPECULARMAP', + ' uniform sampler2D specularMap;', + '#endif' + ].join( '\n' ); + + const glossinessMapParsFragmentChunk = [ + '#ifdef USE_GLOSSINESSMAP', + ' uniform sampler2D glossinessMap;', + '#endif' + ].join( '\n' ); + + const specularMapFragmentChunk = [ + 'vec3 specularFactor = specular;', + '#ifdef USE_SPECULARMAP', + ' vec4 texelSpecular = texture2D( specularMap, vUv );', + ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' specularFactor *= texelSpecular.rgb;', + '#endif' + ].join( '\n' ); + + const glossinessMapFragmentChunk = [ + 'float glossinessFactor = glossiness;', + '#ifdef USE_GLOSSINESSMAP', + ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );', + ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', + ' glossinessFactor *= texelGlossiness.a;', + '#endif' + ].join( '\n' ); + + const lightPhysicalFragmentChunk = [ + 'PhysicalMaterial material;', + 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', + 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', + 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', + 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', + 'material.roughness += geometryRoughness;', + 'material.roughness = min( material.roughness, 1.0 );', + 'material.specularColor = specularFactor;', + ].join( '\n' ); + + const uniforms = { + specular: { value: new Color().setHex( 0xffffff ) }, + glossiness: { value: 1 }, + specularMap: { value: null }, + glossinessMap: { value: null } + }; + + this._extraUniforms = uniforms; + + this.onBeforeCompile = function ( shader ) { + + for ( const uniformName in uniforms ) { + + shader.uniforms[ uniformName ] = uniforms[ uniformName ]; + + } + + shader.fragmentShader = shader.fragmentShader + .replace( 'uniform float roughness;', 'uniform vec3 specular;' ) + .replace( 'uniform float metalness;', 'uniform float glossiness;' ) + .replace( '#include ', specularMapParsFragmentChunk ) + .replace( '#include ', glossinessMapParsFragmentChunk ) + .replace( '#include ', specularMapFragmentChunk ) + .replace( '#include ', glossinessMapFragmentChunk ) + .replace( '#include ', lightPhysicalFragmentChunk ); + + }; + + Object.defineProperties( this, { + + specular: { + get: function () { + + return uniforms.specular.value; + + }, + set: function ( v ) { + + uniforms.specular.value = v; + + } + }, + + specularMap: { + get: function () { + + return uniforms.specularMap.value; + + }, + set: function ( v ) { + + uniforms.specularMap.value = v; + + if ( v ) { + + this.defines.USE_SPECULARMAP = ''; // USE_UV is set by the renderer for specular maps + + } else { + + delete this.defines.USE_SPECULARMAP; + + } + + } + }, + + glossiness: { + get: function () { + + return uniforms.glossiness.value; + + }, + set: function ( v ) { + + uniforms.glossiness.value = v; + + } + }, + + glossinessMap: { + get: function () { + + return uniforms.glossinessMap.value; + + }, + set: function ( v ) { + + uniforms.glossinessMap.value = v; + + if ( v ) { + + this.defines.USE_GLOSSINESSMAP = ''; + this.defines.USE_UV = ''; + + } else { + + delete this.defines.USE_GLOSSINESSMAP; + delete this.defines.USE_UV; + + } + + } + } + + } ); + + delete this.metalness; + delete this.roughness; + delete this.metalnessMap; + delete this.roughnessMap; + + this.setValues( params ); + + } + + copy( source ) { + + super.copy( source ); + + this.specularMap = source.specularMap; + this.specular.copy( source.specular ); + this.glossinessMap = source.glossinessMap; + this.glossiness = source.glossiness; + delete this.metalness; + delete this.roughness; + delete this.metalnessMap; + delete this.roughnessMap; + return this; + + } + +} + + +class GLTFMaterialsPbrSpecularGlossinessExtension { + + constructor() { + + this.name = EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS; + + this.specularGlossinessParams = [ + 'color', + 'map', + 'lightMap', + 'lightMapIntensity', + 'aoMap', + 'aoMapIntensity', + 'emissive', + 'emissiveIntensity', + 'emissiveMap', + 'bumpMap', + 'bumpScale', + 'normalMap', + 'normalMapType', + 'displacementMap', + 'displacementScale', + 'displacementBias', + 'specularMap', + 'specular', + 'glossinessMap', + 'glossiness', + 'alphaMap', + 'envMap', + 'envMapIntensity' + ]; + + } + + getMaterialType() { + + return GLTFMeshStandardSGMaterial; + + } + + extendParams( materialParams, materialDef, parser ) { + + const pbrSpecularGlossiness = materialDef.extensions[ this.name ]; + + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + const pending = []; + + if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) { + + const array = pbrSpecularGlossiness.diffuseFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture, sRGBEncoding ) ); + + } + + materialParams.emissive = new Color( 0.0, 0.0, 0.0 ); + materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0; + materialParams.specular = new Color( 1.0, 1.0, 1.0 ); + + if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) { + + materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor ); + + } + + if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) { + + const specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture; + pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) ); + pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef, sRGBEncoding ) ); + + } + + return Promise.all( pending ); + + } + + createMaterial( materialParams ) { + + const material = new GLTFMeshStandardSGMaterial( materialParams ); + material.fog = true; + + material.color = materialParams.color; + + material.map = materialParams.map === undefined ? null : materialParams.map; + + material.lightMap = null; + material.lightMapIntensity = 1.0; + + material.aoMap = materialParams.aoMap === undefined ? null : materialParams.aoMap; + material.aoMapIntensity = 1.0; + + material.emissive = materialParams.emissive; + material.emissiveIntensity = 1.0; + material.emissiveMap = materialParams.emissiveMap === undefined ? null : materialParams.emissiveMap; + + material.bumpMap = materialParams.bumpMap === undefined ? null : materialParams.bumpMap; + material.bumpScale = 1; + + material.normalMap = materialParams.normalMap === undefined ? null : materialParams.normalMap; + material.normalMapType = TangentSpaceNormalMap; + + if ( materialParams.normalScale ) material.normalScale = materialParams.normalScale; + + material.displacementMap = null; + material.displacementScale = 1; + material.displacementBias = 0; + + material.specularMap = materialParams.specularMap === undefined ? null : materialParams.specularMap; + material.specular = materialParams.specular; + + material.glossinessMap = materialParams.glossinessMap === undefined ? null : materialParams.glossinessMap; + material.glossiness = materialParams.glossiness; + + material.alphaMap = null; + + material.envMap = materialParams.envMap === undefined ? null : materialParams.envMap; + material.envMapIntensity = 1.0; + + return material; + + } + +} + +/** + * Mesh Quantization Extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization + */ +class GLTFMeshQuantizationExtension { + + constructor() { + + this.name = EXTENSIONS$1.KHR_MESH_QUANTIZATION; + + } + +} + +/*********************************/ +/********** INTERPOLATION ********/ +/*********************************/ + +// Spline Interpolation +// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation +class GLTFCubicSplineInterpolant extends Interpolant { + + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); + + } + + copySampleValue_( index ) { + + // Copies a sample value to the result buffer. See description of glTF + // CUBICSPLINE values layout in interpolate_() function below. + + const result = this.resultBuffer, + values = this.sampleValues, + valueSize = this.valueSize, + offset = index * valueSize * 3 + valueSize; + + for ( let i = 0; i !== valueSize; i ++ ) { + + result[ i ] = values[ offset + i ]; + + } + + return result; + + } + +} + +GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + +GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_; + +GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) { + + const result = this.resultBuffer; + const values = this.sampleValues; + const stride = this.valueSize; + + const stride2 = stride * 2; + const stride3 = stride * 3; + + const td = t1 - t0; + + const p = ( t - t0 ) / td; + const pp = p * p; + const ppp = pp * p; + + const offset1 = i1 * stride3; + const offset0 = offset1 - stride3; + + const s2 = - 2 * ppp + 3 * pp; + const s3 = ppp - pp; + const s0 = 1 - s2; + const s1 = s3 - pp + p; + + // Layout of keyframe output values for CUBICSPLINE animations: + // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ] + for ( let i = 0; i !== stride; i ++ ) { + + const p0 = values[ offset0 + i + stride ]; // splineVertex_k + const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k) + const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1 + const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k) + + result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1; + + } + + return result; + +}; + +const _q = new Quaternion(); + +class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { + + interpolate_( i1, t0, t, t1 ) { + + const result = super.interpolate_( i1, t0, t, t1 ); + + _q.fromArray( result ).normalize().toArray( result ); + + return result; + + } + +} + + +/*********************************/ +/********** INTERNALS ************/ +/*********************************/ + +/* CONSTANTS */ + +const WEBGL_CONSTANTS$1 = { + FLOAT: 5126, + //FLOAT_MAT2: 35674, + FLOAT_MAT3: 35675, + FLOAT_MAT4: 35676, + FLOAT_VEC2: 35664, + FLOAT_VEC3: 35665, + FLOAT_VEC4: 35666, + LINEAR: 9729, + REPEAT: 10497, + SAMPLER_2D: 35678, + POINTS: 0, + LINES: 1, + LINE_LOOP: 2, + LINE_STRIP: 3, + TRIANGLES: 4, + TRIANGLE_STRIP: 5, + TRIANGLE_FAN: 6, + UNSIGNED_BYTE: 5121, + UNSIGNED_SHORT: 5123 +}; + +const WEBGL_COMPONENT_TYPES$1 = { + 5120: Int8Array, + 5121: Uint8Array, + 5122: Int16Array, + 5123: Uint16Array, + 5125: Uint32Array, + 5126: Float32Array +}; + +const WEBGL_FILTERS$1 = { + 9728: NearestFilter, + 9729: LinearFilter, + 9984: NearestMipmapNearestFilter, + 9985: LinearMipmapNearestFilter, + 9986: NearestMipmapLinearFilter, + 9987: LinearMipmapLinearFilter +}; + +const WEBGL_WRAPPINGS$1 = { + 33071: ClampToEdgeWrapping, + 33648: MirroredRepeatWrapping, + 10497: RepeatWrapping +}; + +const WEBGL_TYPE_SIZES$1 = { + 'SCALAR': 1, + 'VEC2': 2, + 'VEC3': 3, + 'VEC4': 4, + 'MAT2': 4, + 'MAT3': 9, + 'MAT4': 16 +}; + +const ATTRIBUTES = { + POSITION: 'position', + NORMAL: 'normal', + TANGENT: 'tangent', + TEXCOORD_0: 'uv', + TEXCOORD_1: 'uv2', + COLOR_0: 'color', + WEIGHTS_0: 'skinWeight', + JOINTS_0: 'skinIndex', +}; + +const PATH_PROPERTIES$1 = { + scale: 'scale', + translation: 'position', + rotation: 'quaternion', + weights: 'morphTargetInfluences' +}; + +const INTERPOLATION$1 = { + CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each + // keyframe track will be initialized with a default interpolation type, then modified. + LINEAR: InterpolateLinear, + STEP: InterpolateDiscrete +}; + +const ALPHA_MODES = { + OPAQUE: 'OPAQUE', + MASK: 'MASK', + BLEND: 'BLEND' +}; + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material + */ +function createDefaultMaterial$1( cache ) { + + if ( cache[ 'DefaultMaterial' ] === undefined ) { + + cache[ 'DefaultMaterial' ] = new MeshStandardMaterial( { + color: 0xFFFFFF, + emissive: 0x000000, + metalness: 1, + roughness: 1, + transparent: false, + depthTest: true, + side: FrontSide + } ); + + } + + return cache[ 'DefaultMaterial' ]; + +} + +function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) { + + // Add unknown glTF extensions to an object's userData. + + for ( const name in objectDef.extensions ) { + + if ( knownExtensions[ name ] === undefined ) { + + object.userData.gltfExtensions = object.userData.gltfExtensions || {}; + object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ]; + + } + + } + +} + +/** + * @param {Object3D|Material|BufferGeometry} object + * @param {GLTF.definition} gltfDef + */ +function assignExtrasToUserData( object, gltfDef ) { + + if ( gltfDef.extras !== undefined ) { + + if ( typeof gltfDef.extras === 'object' ) { + + Object.assign( object.userData, gltfDef.extras ); + + } else { + + console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras ); + + } + + } + +} + +/** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets + * + * @param {BufferGeometry} geometry + * @param {Array} targets + * @param {GLTFParser} parser + * @return {Promise} + */ +function addMorphTargets( geometry, targets, parser ) { + + let hasMorphPosition = false; + let hasMorphNormal = false; + let hasMorphColor = false; + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( target.POSITION !== undefined ) hasMorphPosition = true; + if ( target.NORMAL !== undefined ) hasMorphNormal = true; + if ( target.COLOR_0 !== undefined ) hasMorphColor = true; + + if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break; + + } + + if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry ); + + const pendingPositionAccessors = []; + const pendingNormalAccessors = []; + const pendingColorAccessors = []; + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( hasMorphPosition ) { + + const pendingAccessor = target.POSITION !== undefined + ? parser.getDependency( 'accessor', target.POSITION ) + : geometry.attributes.position; + + pendingPositionAccessors.push( pendingAccessor ); + + } + + if ( hasMorphNormal ) { + + const pendingAccessor = target.NORMAL !== undefined + ? parser.getDependency( 'accessor', target.NORMAL ) + : geometry.attributes.normal; + + pendingNormalAccessors.push( pendingAccessor ); + + } + + if ( hasMorphColor ) { + + const pendingAccessor = target.COLOR_0 !== undefined + ? parser.getDependency( 'accessor', target.COLOR_0 ) + : geometry.attributes.color; + + pendingColorAccessors.push( pendingAccessor ); + + } + + } + + return Promise.all( [ + Promise.all( pendingPositionAccessors ), + Promise.all( pendingNormalAccessors ), + Promise.all( pendingColorAccessors ) + ] ).then( function ( accessors ) { + + const morphPositions = accessors[ 0 ]; + const morphNormals = accessors[ 1 ]; + const morphColors = accessors[ 2 ]; + + if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions; + if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals; + if ( hasMorphColor ) geometry.morphAttributes.color = morphColors; + geometry.morphTargetsRelative = true; + + return geometry; + + } ); + +} + +/** + * @param {Mesh} mesh + * @param {GLTF.Mesh} meshDef + */ +function updateMorphTargets( mesh, meshDef ) { + + mesh.updateMorphTargets(); + + if ( meshDef.weights !== undefined ) { + + for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) { + + mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ]; + + } + + } + + // .extras has user-defined data, so check that .extras.targetNames is an array. + if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) { + + const targetNames = meshDef.extras.targetNames; + + if ( mesh.morphTargetInfluences.length === targetNames.length ) { + + mesh.morphTargetDictionary = {}; + + for ( let i = 0, il = targetNames.length; i < il; i ++ ) { + + mesh.morphTargetDictionary[ targetNames[ i ] ] = i; + + } + + } else { + + console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' ); + + } + + } + +} + +function createPrimitiveKey( primitiveDef ) { + + const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ]; + let geometryKey; + + if ( dracoExtension ) { + + geometryKey = 'draco:' + dracoExtension.bufferView + + ':' + dracoExtension.indices + + ':' + createAttributesKey( dracoExtension.attributes ); + + } else { + + geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode; + + } + + return geometryKey; + +} + +function createAttributesKey( attributes ) { + + let attributesKey = ''; + + const keys = Object.keys( attributes ).sort(); + + for ( let i = 0, il = keys.length; i < il; i ++ ) { + + attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';'; + + } + + return attributesKey; + +} + +function getNormalizedComponentScale( constructor ) { + + // Reference: + // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data + + switch ( constructor ) { + + case Int8Array: + return 1 / 127; + + case Uint8Array: + return 1 / 255; + + case Int16Array: + return 1 / 32767; + + case Uint16Array: + return 1 / 65535; + + default: + throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' ); + + } + +} + +function getImageURIMimeType( uri ) { + + if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg'; + if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp'; + + return 'image/png'; + +} + +/* GLTF PARSER */ + +class GLTFParser$1 { + + constructor( json = {}, options = {} ) { + + this.json = json; + this.extensions = {}; + this.plugins = {}; + this.options = options; + + // loader object cache + this.cache = new GLTFRegistry$1(); + + // associations between Three.js objects and glTF elements + this.associations = new Map(); + + // BufferGeometry caching + this.primitiveCache = {}; + + // Object3D instance caches + this.meshCache = { refs: {}, uses: {} }; + this.cameraCache = { refs: {}, uses: {} }; + this.lightCache = { refs: {}, uses: {} }; + + this.sourceCache = {}; + this.textureCache = {}; + + // Track node names, to ensure no duplicates + this.nodeNamesUsed = {}; + + // Use an ImageBitmapLoader if imageBitmaps are supported. Moves much of the + // expensive work of uploading a texture to the GPU off the main thread. + if ( typeof createImageBitmap !== 'undefined' && /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === false ) { + + this.textureLoader = new ImageBitmapLoader( this.options.manager ); + + } else { + + this.textureLoader = new TextureLoader( this.options.manager ); + + } + + this.textureLoader.setCrossOrigin( this.options.crossOrigin ); + this.textureLoader.setRequestHeader( this.options.requestHeader ); + + this.fileLoader = new FileLoader( this.options.manager ); + this.fileLoader.setResponseType( 'arraybuffer' ); + + if ( this.options.crossOrigin === 'use-credentials' ) { + + this.fileLoader.setWithCredentials( true ); + + } + + } + + setExtensions( extensions ) { + + this.extensions = extensions; + + } + + setPlugins( plugins ) { + + this.plugins = plugins; + + } + + parse( onLoad, onError ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + + // Clear the loader cache + this.cache.removeAll(); + + // Mark the special nodes/meshes in json for efficient parse + this._invokeAll( function ( ext ) { + + return ext._markDefs && ext._markDefs(); + + } ); + + Promise.all( this._invokeAll( function ( ext ) { + + return ext.beforeRoot && ext.beforeRoot(); + + } ) ).then( function () { + + return Promise.all( [ + + parser.getDependencies( 'scene' ), + parser.getDependencies( 'animation' ), + parser.getDependencies( 'camera' ), + + ] ); + + } ).then( function ( dependencies ) { + + const result = { + scene: dependencies[ 0 ][ json.scene || 0 ], + scenes: dependencies[ 0 ], + animations: dependencies[ 1 ], + cameras: dependencies[ 2 ], + asset: json.asset, + parser: parser, + userData: {} + }; + + addUnknownExtensionsToUserData( extensions, result, json ); + + assignExtrasToUserData( result, json ); + + Promise.all( parser._invokeAll( function ( ext ) { + + return ext.afterRoot && ext.afterRoot( result ); + + } ) ).then( function () { + + onLoad( result ); + + } ); + + } ).catch( onError ); + + } + + /** + * Marks the special nodes/meshes in json for efficient parse. + */ + _markDefs() { + + const nodeDefs = this.json.nodes || []; + const skinDefs = this.json.skins || []; + const meshDefs = this.json.meshes || []; + + // Nothing in the node definition indicates whether it is a Bone or an + // Object3D. Use the skins' joint references to mark bones. + for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) { + + const joints = skinDefs[ skinIndex ].joints; + + for ( let i = 0, il = joints.length; i < il; i ++ ) { + + nodeDefs[ joints[ i ] ].isBone = true; + + } + + } + + // Iterate over all nodes, marking references to shared resources, + // as well as skeleton joints. + for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) { + + const nodeDef = nodeDefs[ nodeIndex ]; + + if ( nodeDef.mesh !== undefined ) { + + this._addNodeRef( this.meshCache, nodeDef.mesh ); + + // Nothing in the mesh definition indicates whether it is + // a SkinnedMesh or Mesh. Use the node's mesh reference + // to mark SkinnedMesh if node has skin. + if ( nodeDef.skin !== undefined ) { + + meshDefs[ nodeDef.mesh ].isSkinnedMesh = true; + + } + + } + + if ( nodeDef.camera !== undefined ) { + + this._addNodeRef( this.cameraCache, nodeDef.camera ); + + } + + } + + } + + /** + * Counts references to shared node / Object3D resources. These resources + * can be reused, or "instantiated", at multiple nodes in the scene + * hierarchy. Mesh, Camera, and Light instances are instantiated and must + * be marked. Non-scenegraph resources (like Materials, Geometries, and + * Textures) can be reused directly and are not marked here. + * + * Example: CesiumMilkTruck sample model reuses "Wheel" meshes. + */ + _addNodeRef( cache, index ) { + + if ( index === undefined ) return; + + if ( cache.refs[ index ] === undefined ) { + + cache.refs[ index ] = cache.uses[ index ] = 0; + + } + + cache.refs[ index ] ++; + + } + + /** Returns a reference to a shared resource, cloning it if necessary. */ + _getNodeRef( cache, index, object ) { + + if ( cache.refs[ index ] <= 1 ) return object; + + const ref = object.clone(); + + // Propagates mappings to the cloned object, prevents mappings on the + // original object from being lost. + const updateMappings = ( original, clone ) => { + + const mappings = this.associations.get( original ); + if ( mappings != null ) { + + this.associations.set( clone, mappings ); + + } + + for ( const [ i, child ] of original.children.entries() ) { + + updateMappings( child, clone.children[ i ] ); + + } + + }; + + updateMappings( object, ref ); + + ref.name += '_instance_' + ( cache.uses[ index ] ++ ); + + return ref; + + } + + _invokeOne( func ) { + + const extensions = Object.values( this.plugins ); + extensions.push( this ); + + for ( let i = 0; i < extensions.length; i ++ ) { + + const result = func( extensions[ i ] ); + + if ( result ) return result; + + } + + return null; + + } + + _invokeAll( func ) { + + const extensions = Object.values( this.plugins ); + extensions.unshift( this ); + + const pending = []; + + for ( let i = 0; i < extensions.length; i ++ ) { + + const result = func( extensions[ i ] ); + + if ( result ) pending.push( result ); + + } + + return pending; + + } + + /** + * Requests the specified dependency asynchronously, with caching. + * @param {string} type + * @param {number} index + * @return {Promise} + */ + getDependency( type, index ) { + + const cacheKey = type + ':' + index; + let dependency = this.cache.get( cacheKey ); + + if ( ! dependency ) { + + switch ( type ) { + + case 'scene': + dependency = this.loadScene( index ); + break; + + case 'node': + dependency = this.loadNode( index ); + break; + + case 'mesh': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadMesh && ext.loadMesh( index ); + + } ); + break; + + case 'accessor': + dependency = this.loadAccessor( index ); + break; + + case 'bufferView': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadBufferView && ext.loadBufferView( index ); + + } ); + break; + + case 'buffer': + dependency = this.loadBuffer( index ); + break; + + case 'material': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadMaterial && ext.loadMaterial( index ); + + } ); + break; + + case 'texture': + dependency = this._invokeOne( function ( ext ) { + + return ext.loadTexture && ext.loadTexture( index ); + + } ); + break; + + case 'skin': + dependency = this.loadSkin( index ); + break; + + case 'animation': + dependency = this.loadAnimation( index ); + break; + + case 'camera': + dependency = this.loadCamera( index ); + break; + + default: + throw new Error( 'Unknown type: ' + type ); + + } + + this.cache.add( cacheKey, dependency ); + + } + + return dependency; + + } + + /** + * Requests all dependencies of the specified type asynchronously, with caching. + * @param {string} type + * @return {Promise>} + */ + getDependencies( type ) { + + let dependencies = this.cache.get( type ); + + if ( ! dependencies ) { + + const parser = this; + const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || []; + + dependencies = Promise.all( defs.map( function ( def, index ) { + + return parser.getDependency( type, index ); + + } ) ); + + this.cache.add( type, dependencies ); + + } + + return dependencies; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferIndex + * @return {Promise} + */ + loadBuffer( bufferIndex ) { + + const bufferDef = this.json.buffers[ bufferIndex ]; + const loader = this.fileLoader; + + if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) { + + throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' ); + + } + + // If present, GLB container is required to be the first buffer. + if ( bufferDef.uri === undefined && bufferIndex === 0 ) { + + return Promise.resolve( this.extensions[ EXTENSIONS$1.KHR_BINARY_GLTF ].body ); + + } + + const options = this.options; + + return new Promise( function ( resolve, reject ) { + + loader.load( LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () { + + reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) ); + + } ); + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views + * @param {number} bufferViewIndex + * @return {Promise} + */ + loadBufferView( bufferViewIndex ) { + + const bufferViewDef = this.json.bufferViews[ bufferViewIndex ]; + + return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) { + + const byteLength = bufferViewDef.byteLength || 0; + const byteOffset = bufferViewDef.byteOffset || 0; + return buffer.slice( byteOffset, byteOffset + byteLength ); + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors + * @param {number} accessorIndex + * @return {Promise} + */ + loadAccessor( accessorIndex ) { + + const parser = this; + const json = this.json; + + const accessorDef = this.json.accessors[ accessorIndex ]; + + if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) { + + // Ignore empty accessors, which may be used to declare runtime + // information about attributes coming from another source (e.g. Draco + // compression extension). + return Promise.resolve( null ); + + } + + const pendingBufferViews = []; + + if ( accessorDef.bufferView !== undefined ) { + + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) ); + + } else { + + pendingBufferViews.push( null ); + + } + + if ( accessorDef.sparse !== undefined ) { + + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) ); + pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) ); + + } + + return Promise.all( pendingBufferViews ).then( function ( bufferViews ) { + + const bufferView = bufferViews[ 0 ]; + + const itemSize = WEBGL_TYPE_SIZES$1[ accessorDef.type ]; + const TypedArray = WEBGL_COMPONENT_TYPES$1[ accessorDef.componentType ]; + + // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12. + const elementBytes = TypedArray.BYTES_PER_ELEMENT; + const itemBytes = elementBytes * itemSize; + const byteOffset = accessorDef.byteOffset || 0; + const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined; + const normalized = accessorDef.normalized === true; + let array, bufferAttribute; + + // The buffer is not interleaved if the stride is the item size in bytes. + if ( byteStride && byteStride !== itemBytes ) { + + // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer + // This makes sure that IBA.count reflects accessor.count properly + const ibSlice = Math.floor( byteOffset / byteStride ); + const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count; + let ib = parser.cache.get( ibCacheKey ); + + if ( ! ib ) { + + array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes ); + + // Integer parameters to IB/IBA are in array elements, not bytes. + ib = new InterleavedBuffer( array, byteStride / elementBytes ); + + parser.cache.add( ibCacheKey, ib ); + + } + + bufferAttribute = new InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized ); + + } else { + + if ( bufferView === null ) { + + array = new TypedArray( accessorDef.count * itemSize ); + + } else { + + array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize ); + + } + + bufferAttribute = new BufferAttribute( array, itemSize, normalized ); + + } + + // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors + if ( accessorDef.sparse !== undefined ) { + + const itemSizeIndices = WEBGL_TYPE_SIZES$1.SCALAR; + const TypedArrayIndices = WEBGL_COMPONENT_TYPES$1[ accessorDef.sparse.indices.componentType ]; + + const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0; + const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0; + + const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices ); + const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize ); + + if ( bufferView !== null ) { + + // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes. + bufferAttribute = new BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized ); + + } + + for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) { + + const index = sparseIndices[ i ]; + + bufferAttribute.setX( index, sparseValues[ i * itemSize ] ); + if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] ); + if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] ); + if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] ); + if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.' ); + + } + + } + + return bufferAttribute; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures + * @param {number} textureIndex + * @return {Promise} + */ + loadTexture( textureIndex ) { + + const json = this.json; + const options = this.options; + const textureDef = json.textures[ textureIndex ]; + const sourceIndex = textureDef.source; + const sourceDef = json.images[ sourceIndex ]; + + let loader = this.textureLoader; + + if ( sourceDef.uri ) { + + const handler = options.manager.getHandler( sourceDef.uri ); + if ( handler !== null ) loader = handler; + + } + + return this.loadTextureImage( textureIndex, sourceIndex, loader ); + + } + + loadTextureImage( textureIndex, sourceIndex, loader ) { + + const parser = this; + const json = this.json; + + const textureDef = json.textures[ textureIndex ]; + const sourceDef = json.images[ sourceIndex ]; + + const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler; + + if ( this.textureCache[ cacheKey ] ) { + + // See https://github.com/mrdoob/three.js/issues/21559. + return this.textureCache[ cacheKey ]; + + } + + const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) { + + texture.flipY = false; + + if ( textureDef.name ) texture.name = textureDef.name; + + const samplers = json.samplers || {}; + const sampler = samplers[ textureDef.sampler ] || {}; + + texture.magFilter = WEBGL_FILTERS$1[ sampler.magFilter ] || LinearFilter; + texture.minFilter = WEBGL_FILTERS$1[ sampler.minFilter ] || LinearMipmapLinearFilter; + texture.wrapS = WEBGL_WRAPPINGS$1[ sampler.wrapS ] || RepeatWrapping; + texture.wrapT = WEBGL_WRAPPINGS$1[ sampler.wrapT ] || RepeatWrapping; + + parser.associations.set( texture, { textures: textureIndex } ); + + return texture; + + } ).catch( function () { + + return null; + + } ); + + this.textureCache[ cacheKey ] = promise; + + return promise; + + } + + loadImageSource( sourceIndex, loader ) { + + const parser = this; + const json = this.json; + const options = this.options; + + if ( this.sourceCache[ sourceIndex ] !== undefined ) { + + return this.sourceCache[ sourceIndex ].then( ( texture ) => texture.clone() ); + + } + + const sourceDef = json.images[ sourceIndex ]; + + const URL = self.URL || self.webkitURL; + + let sourceURI = sourceDef.uri || ''; + let isObjectURL = false; + + if ( sourceDef.bufferView !== undefined ) { + + // Load binary image data from bufferView, if provided. + + sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) { + + isObjectURL = true; + const blob = new Blob( [ bufferView ], { type: sourceDef.mimeType } ); + sourceURI = URL.createObjectURL( blob ); + return sourceURI; + + } ); + + } else if ( sourceDef.uri === undefined ) { + + throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' ); + + } + + const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) { + + return new Promise( function ( resolve, reject ) { + + let onLoad = resolve; + + if ( loader.isImageBitmapLoader === true ) { + + onLoad = function ( imageBitmap ) { + + const texture = new Texture( imageBitmap ); + texture.needsUpdate = true; + + resolve( texture ); + + }; + + } + + loader.load( LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject ); + + } ); + + } ).then( function ( texture ) { + + // Clean up resources and configure Texture. + + if ( isObjectURL === true ) { + + URL.revokeObjectURL( sourceURI ); + + } + + texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri ); + + return texture; + + } ).catch( function ( error ) { + + console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI ); + throw error; + + } ); + + this.sourceCache[ sourceIndex ] = promise; + return promise; + + } + + /** + * Asynchronously assigns a texture to the given material parameters. + * @param {Object} materialParams + * @param {string} mapName + * @param {Object} mapDef + * @return {Promise} + */ + assignTexture( materialParams, mapName, mapDef, encoding ) { + + const parser = this; + + return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) { + + // Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured + // However, we will copy UV set 0 to UV set 1 on demand for aoMap + if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) { + + console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' ); + + } + + if ( parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] ) { + + const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ] : undefined; + + if ( transform ) { + + const gltfReference = parser.associations.get( texture ); + texture = parser.extensions[ EXTENSIONS$1.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform ); + parser.associations.set( texture, gltfReference ); + + } + + } + + if ( encoding !== undefined ) { + + texture.encoding = encoding; + + } + + materialParams[ mapName ] = texture; + + return texture; + + } ); + + } + + /** + * Assigns final material to a Mesh, Line, or Points instance. The instance + * already has a material (generated from the glTF material options alone) + * but reuse of the same glTF material may require multiple threejs materials + * to accommodate different primitive types, defines, etc. New materials will + * be created if necessary, and reused from a cache. + * @param {Object3D} mesh Mesh, Line, or Points instance. + */ + assignFinalMaterial( mesh ) { + + const geometry = mesh.geometry; + let material = mesh.material; + + const useDerivativeTangents = geometry.attributes.tangent === undefined; + const useVertexColors = geometry.attributes.color !== undefined; + const useFlatShading = geometry.attributes.normal === undefined; + + if ( mesh.isPoints ) { + + const cacheKey = 'PointsMaterial:' + material.uuid; + + let pointsMaterial = this.cache.get( cacheKey ); + + if ( ! pointsMaterial ) { + + pointsMaterial = new PointsMaterial(); + Material.prototype.copy.call( pointsMaterial, material ); + pointsMaterial.color.copy( material.color ); + pointsMaterial.map = material.map; + pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px + + this.cache.add( cacheKey, pointsMaterial ); + + } + + material = pointsMaterial; + + } else if ( mesh.isLine ) { + + const cacheKey = 'LineBasicMaterial:' + material.uuid; + + let lineMaterial = this.cache.get( cacheKey ); + + if ( ! lineMaterial ) { + + lineMaterial = new LineBasicMaterial(); + Material.prototype.copy.call( lineMaterial, material ); + lineMaterial.color.copy( material.color ); + + this.cache.add( cacheKey, lineMaterial ); + + } + + material = lineMaterial; + + } + + // Clone the material if it will be modified + if ( useDerivativeTangents || useVertexColors || useFlatShading ) { + + let cacheKey = 'ClonedMaterial:' + material.uuid + ':'; + + if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:'; + if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:'; + if ( useVertexColors ) cacheKey += 'vertex-colors:'; + if ( useFlatShading ) cacheKey += 'flat-shading:'; + + let cachedMaterial = this.cache.get( cacheKey ); + + if ( ! cachedMaterial ) { + + cachedMaterial = material.clone(); + + if ( useVertexColors ) cachedMaterial.vertexColors = true; + if ( useFlatShading ) cachedMaterial.flatShading = true; + + if ( useDerivativeTangents ) { + + // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 + if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; + if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; + + } + + this.cache.add( cacheKey, cachedMaterial ); + + this.associations.set( cachedMaterial, this.associations.get( material ) ); + + } + + material = cachedMaterial; + + } + + // workarounds for mesh and geometry + + if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) { + + geometry.setAttribute( 'uv2', geometry.attributes.uv ); + + } + + mesh.material = material; + + } + + getMaterialType( /* materialIndex */ ) { + + return MeshStandardMaterial; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials + * @param {number} materialIndex + * @return {Promise} + */ + loadMaterial( materialIndex ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + const materialDef = json.materials[ materialIndex ]; + + let materialType; + const materialParams = {}; + const materialExtensions = materialDef.extensions || {}; + + const pending = []; + + if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) { + + const sgExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ]; + materialType = sgExtension.getMaterialType(); + pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) ); + + } else if ( materialExtensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ] ) { + + const kmuExtension = extensions[ EXTENSIONS$1.KHR_MATERIALS_UNLIT ]; + materialType = kmuExtension.getMaterialType(); + pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) ); + + } else { + + // Specification: + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material + + const metallicRoughness = materialDef.pbrMetallicRoughness || {}; + + materialParams.color = new Color( 1.0, 1.0, 1.0 ); + materialParams.opacity = 1.0; + + if ( Array.isArray( metallicRoughness.baseColorFactor ) ) { + + const array = metallicRoughness.baseColorFactor; + + materialParams.color.fromArray( array ); + materialParams.opacity = array[ 3 ]; + + } + + if ( metallicRoughness.baseColorTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, sRGBEncoding ) ); + + } + + materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0; + materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0; + + if ( metallicRoughness.metallicRoughnessTexture !== undefined ) { + + pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) ); + pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) ); + + } + + materialType = this._invokeOne( function ( ext ) { + + return ext.getMaterialType && ext.getMaterialType( materialIndex ); + + } ); + + pending.push( Promise.all( this._invokeAll( function ( ext ) { + + return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams ); + + } ) ) ); + + } + + if ( materialDef.doubleSided === true ) { + + materialParams.side = DoubleSide; + + } + + const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE; + + if ( alphaMode === ALPHA_MODES.BLEND ) { + + materialParams.transparent = true; + + // See: https://github.com/mrdoob/three.js/issues/17706 + materialParams.depthWrite = false; + + } else { + + materialParams.transparent = false; + + if ( alphaMode === ALPHA_MODES.MASK ) { + + materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5; + + } + + } + + if ( materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); + + materialParams.normalScale = new Vector2( 1, 1 ); + + if ( materialDef.normalTexture.scale !== undefined ) { + + const scale = materialDef.normalTexture.scale; + + materialParams.normalScale.set( scale, scale ); + + } + + } + + if ( materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) ); + + if ( materialDef.occlusionTexture.strength !== undefined ) { + + materialParams.aoMapIntensity = materialDef.occlusionTexture.strength; + + } + + } + + if ( materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial ) { + + materialParams.emissive = new Color().fromArray( materialDef.emissiveFactor ); + + } + + if ( materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial ) { + + pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, sRGBEncoding ) ); + + } + + return Promise.all( pending ).then( function () { + + let material; + + if ( materialType === GLTFMeshStandardSGMaterial ) { + + material = extensions[ EXTENSIONS$1.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams ); + + } else { + + material = new materialType( materialParams ); + + } + + if ( materialDef.name ) material.name = materialDef.name; + + assignExtrasToUserData( material, materialDef ); + + parser.associations.set( material, { materials: materialIndex } ); + + if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef ); + + return material; + + } ); + + } + + /** When Object3D instances are targeted by animation, they need unique names. */ + createUniqueName( originalName ) { + + const sanitizedName = PropertyBinding.sanitizeNodeName( originalName || '' ); + + let name = sanitizedName; + + for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) { + + name = sanitizedName + '_' + i; + + } + + this.nodeNamesUsed[ name ] = true; + + return name; + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry + * + * Creates BufferGeometries from primitives. + * + * @param {Array} primitives + * @return {Promise>} + */ + loadGeometries( primitives ) { + + const parser = this; + const extensions = this.extensions; + const cache = this.primitiveCache; + + function createDracoPrimitive( primitive ) { + + return extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] + .decodePrimitive( primitive, parser ) + .then( function ( geometry ) { + + return addPrimitiveAttributes( geometry, primitive, parser ); + + } ); + + } + + const pending = []; + + for ( let i = 0, il = primitives.length; i < il; i ++ ) { + + const primitive = primitives[ i ]; + const cacheKey = createPrimitiveKey( primitive ); + + // See if we've already created this geometry + const cached = cache[ cacheKey ]; + + if ( cached ) { + + // Use the cached geometry if it exists + pending.push( cached.promise ); + + } else { + + let geometryPromise; + + if ( primitive.extensions && primitive.extensions[ EXTENSIONS$1.KHR_DRACO_MESH_COMPRESSION ] ) { + + // Use DRACO geometry if available + geometryPromise = createDracoPrimitive( primitive ); + + } else { + + // Otherwise create a new geometry + geometryPromise = addPrimitiveAttributes( new BufferGeometry(), primitive, parser ); + + } + + // Cache this geometry + cache[ cacheKey ] = { primitive: primitive, promise: geometryPromise }; + + pending.push( geometryPromise ); + + } + + } + + return Promise.all( pending ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes + * @param {number} meshIndex + * @return {Promise} + */ + loadMesh( meshIndex ) { + + const parser = this; + const json = this.json; + const extensions = this.extensions; + + const meshDef = json.meshes[ meshIndex ]; + const primitives = meshDef.primitives; + + const pending = []; + + for ( let i = 0, il = primitives.length; i < il; i ++ ) { + + const material = primitives[ i ].material === undefined + ? createDefaultMaterial$1( this.cache ) + : this.getDependency( 'material', primitives[ i ].material ); + + pending.push( material ); + + } + + pending.push( parser.loadGeometries( primitives ) ); + + return Promise.all( pending ).then( function ( results ) { + + const materials = results.slice( 0, results.length - 1 ); + const geometries = results[ results.length - 1 ]; + + const meshes = []; + + for ( let i = 0, il = geometries.length; i < il; i ++ ) { + + const geometry = geometries[ i ]; + const primitive = primitives[ i ]; + + // 1. create Mesh + + let mesh; + + const material = materials[ i ]; + + if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLES || + primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP || + primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN || + primitive.mode === undefined ) { + + // .isSkinnedMesh isn't in glTF spec. See ._markDefs() + mesh = meshDef.isSkinnedMesh === true + ? new SkinnedMesh( geometry, material ) + : new Mesh( geometry, material ); + + if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) { + + // we normalize floating point skin weight array to fix malformed assets (see #15319) + // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs + mesh.normalizeSkinWeights(); + + } + + if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_STRIP ) { + + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleStripDrawMode ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.TRIANGLE_FAN ) { + + mesh.geometry = toTrianglesDrawMode( mesh.geometry, TriangleFanDrawMode ); + + } + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINES ) { + + mesh = new LineSegments( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_STRIP ) { + + mesh = new Line( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.LINE_LOOP ) { + + mesh = new LineLoop( geometry, material ); + + } else if ( primitive.mode === WEBGL_CONSTANTS$1.POINTS ) { + + mesh = new Points( geometry, material ); + + } else { + + throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode ); + + } + + if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) { + + updateMorphTargets( mesh, meshDef ); + + } + + mesh.name = parser.createUniqueName( meshDef.name || ( 'mesh_' + meshIndex ) ); + + assignExtrasToUserData( mesh, meshDef ); + + if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive ); + + parser.assignFinalMaterial( mesh ); + + meshes.push( mesh ); + + } + + for ( let i = 0, il = meshes.length; i < il; i ++ ) { + + parser.associations.set( meshes[ i ], { + meshes: meshIndex, + primitives: i + } ); + + } + + if ( meshes.length === 1 ) { + + return meshes[ 0 ]; + + } + + const group = new Group(); + + parser.associations.set( group, { meshes: meshIndex } ); + + for ( let i = 0, il = meshes.length; i < il; i ++ ) { + + group.add( meshes[ i ] ); + + } + + return group; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras + * @param {number} cameraIndex + * @return {Promise} + */ + loadCamera( cameraIndex ) { + + let camera; + const cameraDef = this.json.cameras[ cameraIndex ]; + const params = cameraDef[ cameraDef.type ]; + + if ( ! params ) { + + console.warn( 'THREE.GLTFLoader: Missing camera parameters.' ); + return; + + } + + if ( cameraDef.type === 'perspective' ) { + + camera = new PerspectiveCamera( MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 ); + + } else if ( cameraDef.type === 'orthographic' ) { + + camera = new OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar ); + + } + + if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name ); + + assignExtrasToUserData( camera, cameraDef ); + + return Promise.resolve( camera ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins + * @param {number} skinIndex + * @return {Promise} + */ + loadSkin( skinIndex ) { + + const skinDef = this.json.skins[ skinIndex ]; + + const skinEntry = { joints: skinDef.joints }; + + if ( skinDef.inverseBindMatrices === undefined ) { + + return Promise.resolve( skinEntry ); + + } + + return this.getDependency( 'accessor', skinDef.inverseBindMatrices ).then( function ( accessor ) { + + skinEntry.inverseBindMatrices = accessor; + + return skinEntry; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations + * @param {number} animationIndex + * @return {Promise} + */ + loadAnimation( animationIndex ) { + + const json = this.json; + + const animationDef = json.animations[ animationIndex ]; + + const pendingNodes = []; + const pendingInputAccessors = []; + const pendingOutputAccessors = []; + const pendingSamplers = []; + const pendingTargets = []; + + for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) { + + const channel = animationDef.channels[ i ]; + const sampler = animationDef.samplers[ channel.sampler ]; + const target = channel.target; + const name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated. + const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input; + const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output; + + pendingNodes.push( this.getDependency( 'node', name ) ); + pendingInputAccessors.push( this.getDependency( 'accessor', input ) ); + pendingOutputAccessors.push( this.getDependency( 'accessor', output ) ); + pendingSamplers.push( sampler ); + pendingTargets.push( target ); + + } + + return Promise.all( [ + + Promise.all( pendingNodes ), + Promise.all( pendingInputAccessors ), + Promise.all( pendingOutputAccessors ), + Promise.all( pendingSamplers ), + Promise.all( pendingTargets ) + + ] ).then( function ( dependencies ) { + + const nodes = dependencies[ 0 ]; + const inputAccessors = dependencies[ 1 ]; + const outputAccessors = dependencies[ 2 ]; + const samplers = dependencies[ 3 ]; + const targets = dependencies[ 4 ]; + + const tracks = []; + + for ( let i = 0, il = nodes.length; i < il; i ++ ) { + + const node = nodes[ i ]; + const inputAccessor = inputAccessors[ i ]; + const outputAccessor = outputAccessors[ i ]; + const sampler = samplers[ i ]; + const target = targets[ i ]; + + if ( node === undefined ) continue; + + node.updateMatrix(); + node.matrixAutoUpdate = true; + + let TypedKeyframeTrack; + + switch ( PATH_PROPERTIES$1[ target.path ] ) { + + case PATH_PROPERTIES$1.weights: + + TypedKeyframeTrack = NumberKeyframeTrack; + break; + + case PATH_PROPERTIES$1.rotation: + + TypedKeyframeTrack = QuaternionKeyframeTrack; + break; + + case PATH_PROPERTIES$1.position: + case PATH_PROPERTIES$1.scale: + default: + + TypedKeyframeTrack = VectorKeyframeTrack; + break; + + } + + const targetName = node.name ? node.name : node.uuid; + + const interpolation = sampler.interpolation !== undefined ? INTERPOLATION$1[ sampler.interpolation ] : InterpolateLinear; + + const targetNames = []; + + if ( PATH_PROPERTIES$1[ target.path ] === PATH_PROPERTIES$1.weights ) { + + node.traverse( function ( object ) { + + if ( object.morphTargetInfluences ) { + + targetNames.push( object.name ? object.name : object.uuid ); + + } + + } ); + + } else { + + targetNames.push( targetName ); + + } + + let outputArray = outputAccessor.array; + + if ( outputAccessor.normalized ) { + + const scale = getNormalizedComponentScale( outputArray.constructor ); + const scaled = new Float32Array( outputArray.length ); + + for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) { + + scaled[ j ] = outputArray[ j ] * scale; + + } + + outputArray = scaled; + + } + + for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) { + + const track = new TypedKeyframeTrack( + targetNames[ j ] + '.' + PATH_PROPERTIES$1[ target.path ], + inputAccessor.array, + outputArray, + interpolation + ); + + // Override interpolation with custom factory method. + if ( sampler.interpolation === 'CUBICSPLINE' ) { + + track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) { + + // A CUBICSPLINE keyframe in glTF has three output values for each input value, + // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize() + // must be divided by three to get the interpolant's sampleSize argument. + + const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant; + + return new interpolantType( this.times, this.values, this.getValueSize() / 3, result ); + + }; + + // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants. + track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true; + + } + + tracks.push( track ); + + } + + } + + const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex; + + return new AnimationClip( name, undefined, tracks ); + + } ); + + } + + createNodeMesh( nodeIndex ) { + + const json = this.json; + const parser = this; + const nodeDef = json.nodes[ nodeIndex ]; + + if ( nodeDef.mesh === undefined ) return null; + + return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) { + + const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh ); + + // if weights are provided on the node, override weights on the mesh. + if ( nodeDef.weights !== undefined ) { + + node.traverse( function ( o ) { + + if ( ! o.isMesh ) return; + + for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) { + + o.morphTargetInfluences[ i ] = nodeDef.weights[ i ]; + + } + + } ); + + } + + return node; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy + * @param {number} nodeIndex + * @return {Promise} + */ + loadNode( nodeIndex ) { + + const json = this.json; + const extensions = this.extensions; + const parser = this; + + const nodeDef = json.nodes[ nodeIndex ]; + + // reserve node's name before its dependencies, so the root has the intended name. + const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : ''; + + return ( function () { + + const pending = []; + + const meshPromise = parser._invokeOne( function ( ext ) { + + return ext.createNodeMesh && ext.createNodeMesh( nodeIndex ); + + } ); + + if ( meshPromise ) { + + pending.push( meshPromise ); + + } + + if ( nodeDef.camera !== undefined ) { + + pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) { + + return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera ); + + } ) ); + + } + + parser._invokeAll( function ( ext ) { + + return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex ); + + } ).forEach( function ( promise ) { + + pending.push( promise ); + + } ); + + return Promise.all( pending ); + + }() ).then( function ( objects ) { + + let node; + + // .isBone isn't in glTF spec. See ._markDefs + if ( nodeDef.isBone === true ) { + + node = new Bone(); + + } else if ( objects.length > 1 ) { + + node = new Group(); + + } else if ( objects.length === 1 ) { + + node = objects[ 0 ]; + + } else { + + node = new Object3D(); + + } + + if ( node !== objects[ 0 ] ) { + + for ( let i = 0, il = objects.length; i < il; i ++ ) { + + node.add( objects[ i ] ); + + } + + } + + if ( nodeDef.name ) { + + node.userData.name = nodeDef.name; + node.name = nodeName; + + } + + assignExtrasToUserData( node, nodeDef ); + + if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef ); + + if ( nodeDef.matrix !== undefined ) { + + const matrix = new Matrix4(); + matrix.fromArray( nodeDef.matrix ); + node.applyMatrix4( matrix ); + + } else { + + if ( nodeDef.translation !== undefined ) { + + node.position.fromArray( nodeDef.translation ); + + } + + if ( nodeDef.rotation !== undefined ) { + + node.quaternion.fromArray( nodeDef.rotation ); + + } + + if ( nodeDef.scale !== undefined ) { + + node.scale.fromArray( nodeDef.scale ); + + } + + } + + if ( ! parser.associations.has( node ) ) { + + parser.associations.set( node, {} ); + + } + + parser.associations.get( node ).nodes = nodeIndex; + + return node; + + } ); + + } + + /** + * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes + * @param {number} sceneIndex + * @return {Promise} + */ + loadScene( sceneIndex ) { + + const json = this.json; + const extensions = this.extensions; + const sceneDef = this.json.scenes[ sceneIndex ]; + const parser = this; + + // Loader returns Group, not Scene. + // See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172 + const scene = new Group(); + if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name ); + + assignExtrasToUserData( scene, sceneDef ); + + if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef ); + + const nodeIds = sceneDef.nodes || []; + + const pending = []; + + for ( let i = 0, il = nodeIds.length; i < il; i ++ ) { + + pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) ); + + } + + return Promise.all( pending ).then( function () { + + // Removes dangling associations, associations that reference a node that + // didn't make it into the scene. + const reduceAssociations = ( node ) => { + + const reducedAssociations = new Map(); + + for ( const [ key, value ] of parser.associations ) { + + if ( key instanceof Material || key instanceof Texture ) { + + reducedAssociations.set( key, value ); + + } + + } + + node.traverse( ( node ) => { + + const mappings = parser.associations.get( node ); + + if ( mappings != null ) { + + reducedAssociations.set( node, mappings ); + + } + + } ); + + return reducedAssociations; + + }; + + parser.associations = reduceAssociations( scene ); + + return scene; + + } ); + + } + +} + +function buildNodeHierarchy( nodeId, parentObject, json, parser ) { + + const nodeDef = json.nodes[ nodeId ]; + + return parser.getDependency( 'node', nodeId ).then( function ( node ) { + + if ( nodeDef.skin === undefined ) return node; + + // build skeleton here as well + + let skinEntry; + + return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skin ) { + + skinEntry = skin; + + const pendingJoints = []; + + for ( let i = 0, il = skinEntry.joints.length; i < il; i ++ ) { + + pendingJoints.push( parser.getDependency( 'node', skinEntry.joints[ i ] ) ); + + } + + return Promise.all( pendingJoints ); + + } ).then( function ( jointNodes ) { + + node.traverse( function ( mesh ) { + + if ( ! mesh.isMesh ) return; + + const bones = []; + const boneInverses = []; + + for ( let j = 0, jl = jointNodes.length; j < jl; j ++ ) { + + const jointNode = jointNodes[ j ]; + + if ( jointNode ) { + + bones.push( jointNode ); + + const mat = new Matrix4(); + + if ( skinEntry.inverseBindMatrices !== undefined ) { + + mat.fromArray( skinEntry.inverseBindMatrices.array, j * 16 ); + + } + + boneInverses.push( mat ); + + } else { + + console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[ j ] ); + + } + + } + + mesh.bind( new Skeleton( bones, boneInverses ), mesh.matrixWorld ); + + } ); + + return node; + + } ); + + } ).then( function ( node ) { + + // build node hierachy + + parentObject.add( node ); + + const pending = []; + + if ( nodeDef.children ) { + + const children = nodeDef.children; + + for ( let i = 0, il = children.length; i < il; i ++ ) { + + const child = children[ i ]; + pending.push( buildNodeHierarchy( child, node, json, parser ) ); + + } + + } + + return Promise.all( pending ); + + } ); + +} + +/** + * @param {BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + */ +function computeBounds( geometry, primitiveDef, parser ) { + + const attributes = primitiveDef.attributes; + + const box = new Box3(); + + if ( attributes.POSITION !== undefined ) { + + const accessor = parser.json.accessors[ attributes.POSITION ]; + + const min = accessor.min; + const max = accessor.max; + + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + + if ( min !== undefined && max !== undefined ) { + + box.set( + new Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), + new Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) + ); + + if ( accessor.normalized ) { + + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + box.min.multiplyScalar( boxScale ); + box.max.multiplyScalar( boxScale ); + + } + + } else { + + console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + + return; + + } + + } else { + + return; + + } + + const targets = primitiveDef.targets; + + if ( targets !== undefined ) { + + const maxDisplacement = new Vector3(); + const vector = new Vector3(); + + for ( let i = 0, il = targets.length; i < il; i ++ ) { + + const target = targets[ i ]; + + if ( target.POSITION !== undefined ) { + + const accessor = parser.json.accessors[ target.POSITION ]; + const min = accessor.min; + const max = accessor.max; + + // glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement. + + if ( min !== undefined && max !== undefined ) { + + // we need to get max of absolute components because target weight is [-1,1] + vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) ); + vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) ); + vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) ); + + + if ( accessor.normalized ) { + + const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES$1[ accessor.componentType ] ); + vector.multiplyScalar( boxScale ); + + } + + // Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative + // to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets + // are used to implement key-frame animations and as such only two are active at a time - this results in very large + // boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size. + maxDisplacement.max( vector ); + + } else { + + console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' ); + + } + + } + + } + + // As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets. + box.expandByVector( maxDisplacement ); + + } + + geometry.boundingBox = box; + + const sphere = new Sphere(); + + box.getCenter( sphere.center ); + sphere.radius = box.min.distanceTo( box.max ) / 2; + + geometry.boundingSphere = sphere; + +} + +/** + * @param {BufferGeometry} geometry + * @param {GLTF.Primitive} primitiveDef + * @param {GLTFParser} parser + * @return {Promise} + */ +function addPrimitiveAttributes( geometry, primitiveDef, parser ) { + + const attributes = primitiveDef.attributes; + + const pending = []; + + function assignAttributeAccessor( accessorIndex, attributeName ) { + + return parser.getDependency( 'accessor', accessorIndex ) + .then( function ( accessor ) { + + geometry.setAttribute( attributeName, accessor ); + + } ); + + } + + for ( const gltfAttributeName in attributes ) { + + const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase(); + + // Skip attributes already provided by e.g. Draco extension. + if ( threeAttributeName in geometry.attributes ) continue; + + pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) ); + + } + + if ( primitiveDef.indices !== undefined && ! geometry.index ) { + + const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) { + + geometry.setIndex( accessor ); + + } ); + + pending.push( accessor ); + + } + + assignExtrasToUserData( geometry, primitiveDef ); + + computeBounds( geometry, primitiveDef, parser ); + + return Promise.all( pending ).then( function () { + + return primitiveDef.targets !== undefined + ? addMorphTargets( geometry, primitiveDef.targets, parser ) + : geometry; + + } ); + +} + +/** + * @param {BufferGeometry} geometry + * @param {Number} drawMode + * @return {BufferGeometry} + */ +function toTrianglesDrawMode( geometry, drawMode ) { + + let index = geometry.getIndex(); + + // generate index if not present + + if ( index === null ) { + + const indices = []; + + const position = geometry.getAttribute( 'position' ); + + if ( position !== undefined ) { + + for ( let i = 0; i < position.count; i ++ ) { + + indices.push( i ); + + } + + geometry.setIndex( indices ); + index = geometry.getIndex(); + + } else { + + console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' ); + return geometry; + + } + + } + + // + + const numberOfTriangles = index.count - 2; + const newIndices = []; + + if ( drawMode === TriangleFanDrawMode ) { + + // gl.TRIANGLE_FAN + + for ( let i = 1; i <= numberOfTriangles; i ++ ) { + + newIndices.push( index.getX( 0 ) ); + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + + } + + } else { + + // gl.TRIANGLE_STRIP + + for ( let i = 0; i < numberOfTriangles; i ++ ) { + + if ( i % 2 === 0 ) { + + newIndices.push( index.getX( i ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i + 2 ) ); + + + } else { + + newIndices.push( index.getX( i + 2 ) ); + newIndices.push( index.getX( i + 1 ) ); + newIndices.push( index.getX( i ) ); + + } + + } + + } + + if ( ( newIndices.length / 3 ) !== numberOfTriangles ) { + + console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' ); + + } + + // build final geometry + + const newGeometry = geometry.clone(); + newGeometry.setIndex( newIndices ); + + return newGeometry; + +} + +class VRButton { + + static createButton( renderer, options ) { + + if ( options ) { + + console.error( 'THREE.VRButton: The "options" parameter has been removed. Please set the reference space type via renderer.xr.setReferenceSpaceType() instead.' ); + + } + + const button = document.createElement( 'button' ); + + function showEnterVR( /*device*/ ) { + + let currentSession = null; + + async function onSessionStarted( session ) { + + session.addEventListener( 'end', onSessionEnded ); + + await renderer.xr.setSession( session ); + button.textContent = 'EXIT VR'; + + currentSession = session; + + } + + function onSessionEnded( /*event*/ ) { + + currentSession.removeEventListener( 'end', onSessionEnded ); + + button.textContent = 'ENTER VR'; + + currentSession = null; + + } + + // + + button.style.display = ''; + + button.style.cursor = 'pointer'; + button.style.left = 'calc(50% - 50px)'; + button.style.width = '100px'; + + button.textContent = 'ENTER VR'; + + button.onmouseenter = function () { + + button.style.opacity = '1.0'; + + }; + + button.onmouseleave = function () { + + button.style.opacity = '0.5'; + + }; + + button.onclick = function () { + + if ( currentSession === null ) { + + // WebXR's requestReferenceSpace only works if the corresponding feature + // was requested at session creation time. For simplicity, just ask for + // the interesting ones as optional features, but be aware that the + // requestReferenceSpace call will fail if it turns out to be unavailable. + // ('local' is always available for immersive sessions and doesn't need to + // be requested separately.) + + const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor', 'hand-tracking', 'layers' ] }; + navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted ); + + } else { + + currentSession.end(); + + } + + }; + + } + + function disableButton() { + + button.style.display = ''; + + button.style.cursor = 'auto'; + button.style.left = 'calc(50% - 75px)'; + button.style.width = '150px'; + + button.onmouseenter = null; + button.onmouseleave = null; + + button.onclick = null; + + } + + function showWebXRNotFound() { + + disableButton(); + + button.textContent = 'VR NOT SUPPORTED'; + + } + + function showVRNotAllowed( exception ) { + + disableButton(); + + console.warn( 'Exception when trying to call xr.isSessionSupported', exception ); + + button.textContent = 'VR NOT ALLOWED'; + + } + + function stylizeElement( element ) { + + element.style.position = 'absolute'; + element.style.bottom = '20px'; + element.style.padding = '12px 6px'; + element.style.border = '1px solid #fff'; + element.style.borderRadius = '4px'; + element.style.background = 'rgba(0,0,0,0.1)'; + element.style.color = '#fff'; + element.style.font = 'normal 13px sans-serif'; + element.style.textAlign = 'center'; + element.style.opacity = '0.5'; + element.style.outline = 'none'; + element.style.zIndex = '999'; + + } + + if ( 'xr' in navigator ) { + + button.id = 'VRButton'; + button.style.display = 'none'; + + stylizeElement( button ); + + navigator.xr.isSessionSupported( 'immersive-vr' ).then( function ( supported ) { + + supported ? showEnterVR() : showWebXRNotFound(); + + if ( supported && VRButton.xrSessionIsGranted ) { + + button.click(); + + } + + } ).catch( showVRNotAllowed ); + + return button; + + } else { + + const message = document.createElement( 'a' ); + + if ( window.isSecureContext === false ) { + + message.href = document.location.href.replace( /^http:/, 'https:' ); + message.innerHTML = 'WEBXR NEEDS HTTPS'; // TODO Improve message + + } else { + + message.href = 'https://immersiveweb.dev/'; + message.innerHTML = 'WEBXR NOT AVAILABLE'; + + } + + message.style.left = 'calc(50% - 90px)'; + message.style.width = '180px'; + message.style.textDecoration = 'none'; + + stylizeElement( message ); + + return message; + + } + + } + + static xrSessionIsGranted = false; + + static registerSessionGrantedListener() { + + if ( 'xr' in navigator ) { + + navigator.xr.addEventListener( 'sessiongranted', () => { + + VRButton.xrSessionIsGranted = true; + + } ); + + } + + } + +} + +VRButton.registerSessionGrantedListener(); + +/*! + * three-icosa + * https://github.com/icosa-gallery/three-icosa + * Copyright (c) 2021-2022 Icosa Gallery + * Released under the Apache 2.0 Licence. + */ + +// Copyright 2021-2022 Icosa Gallery + +class TiltShaderLoader$1 extends Loader { + constructor( manager ) { + super( manager ); + + this.loadedMaterials = {}; + } + + async load(brushName, onLoad, onProgress, onError ) { + const scope = this; + + const isAlreadyLoaded = this.loadedMaterials[brushName]; + + if (isAlreadyLoaded !== undefined) { + onLoad( scope.parse( isAlreadyLoaded ) ); + return; + } + + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + + const textureLoader = new TextureLoader(this.manager); + textureLoader.setPath(this.path); + textureLoader.setWithCredentials( this.withCredentials ); + + const materialParams = tiltBrushMaterialParams$1[brushName]; + + materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); + materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + + if (materialParams.uniforms.u_MainTex) { + const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); + mainTex.name = `${brushName}_MainTex`; + mainTex.wrapS = RepeatWrapping; + mainTex.wrapT = RepeatWrapping; + mainTex.flipY = false; + materialParams.uniforms.u_MainTex.value = mainTex; + } + + if (materialParams.uniforms.u_BumpMap) { + const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); + bumpMap.name = `${brushName}_BumpMap`; + bumpMap.wrapS = RepeatWrapping; + bumpMap.wrapT = RepeatWrapping; + bumpMap.flipY = false; + materialParams.uniforms.u_BumpMap.value = bumpMap; + } + + if (materialParams.uniforms.u_AlphaMask) { + const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); + alphaMask.name = `${brushName}_AlphaMask`; + alphaMask.wrapS = RepeatWrapping; + alphaMask.wrapT = RepeatWrapping; + alphaMask.flipY = false; + materialParams.uniforms.u_AlphaMask.value = alphaMask; + } + + // inject three.js lighting uniforms + for(var lightType in UniformsLib.lights) + { + materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; + } + + let rawMaterial = new RawShaderMaterial(materialParams); + this.loadedMaterials[brushName] = rawMaterial; + onLoad( scope.parse( rawMaterial ) ); + } + + parse( rawMaterial ) { + return rawMaterial; + } + + lookupMaterial(nameOrGuid) { + const name = this.lookupMaterialName(nameOrGuid); + return tiltBrushMaterialParams$1[name]; + } + + lookupMaterialName(nameOrGuid) { + switch(nameOrGuid) { + case "BlocksBasic:": + case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": + return "BlocksBasic"; + + case "BlocksGem": + case "232998f8-d357-47a2-993a-53415df9be10": + return "BlocksGem"; + + case "BlocksGlass": + case "3d813d82-5839-4450-8ddc-8e889ecd96c7": + return "BlocksGlass"; + + case "Bubbles": + case "89d104cd-d012-426b-b5b3-bbaee63ac43c": + return "Bubbles"; + + case "CelVinyl": + case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": + return "CelVinyl"; + + case "ChromaticWave": + case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": + return "ChromaticWave"; + + case "CoarseBristles": + case "1161af82-50cf-47db-9706-0c3576d43c43": + case "79168f10-6961-464a-8be1-57ed364c5600": + return "CoarseBristles"; + + case "Comet": + case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": + return "Comet"; + + case "DiamondHull": + case "c8313697-2563-47fc-832e-290f4c04b901": + return "DiamondHull"; + + case "Disco": + case "4391aaaa-df73-4396-9e33-31e4e4930b27": + return "Disco"; + + case "DotMarker": + case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": + return "DotMarker"; + + case "Dots": + case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": + return "Dots"; + + case "DoubleTaperedFlat": + case "0d3889f3-3ede-470c-8af4-f44813306126": + return "DoubleTaperedFlat"; + + case "DoubleTaperedMarker": + case "0d3889f3-3ede-470c-8af4-de4813306126": + return "DoubleTaperedMarker"; + + case "DuctTape": + case "d0262945-853c-4481-9cbd-88586bed93cb": + case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": + return "DuctTape"; + + case "Electricity": + case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": + return "Electricity"; + + case "Embers": + case "02ffb866-7fb2-4d15-b761-1012cefb1360": + return "Embers"; + + case "EnvironmentDiffuse": + case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": + return "EnvironmentDiffuse"; + + case "EnvironmentDiffuseLightMap": + case "d01d9d6c-9a61-4aba-8146-5891fafb013b": + return "EnvironmentDiffuseLightMap"; + + case "Fire": + case "cb92b597-94ca-4255-b017-0e3f42f12f9e": + return "Fire"; + + case "2d35bcf0-e4d8-452c-97b1-3311be063130": + case "280c0a7a-aad8-416c-a7d2-df63d129ca70": + case "55303bc4-c749-4a72-98d9-d23e68e76e18": + case "Flat": + return "Flat"; + + case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": + case "geometry_Highlighter": + return "Highlighter"; + + case "Hypercolor": + case "dce872c2-7b49-4684-b59b-c45387949c5c": + case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": + return "Hypercolor"; + + case "HyperGrid": + case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": + return "HyperGrid"; + + case "Icing": + case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": + return "Icing"; + + case "Ink": + case "f5c336cf-5108-4b40-ade9-c687504385ab": + case "c0012095-3ffd-4040-8ee1-fc180d346eaa": + return "Ink"; + + case "Leaves": + case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": + case "ea19de07-d0c0-4484-9198-18489a3c1487": + return "Leaves"; + + case "Light": + case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": + return "Light"; + + case "LightWire": + case "4391aaaa-df81-4396-9e33-31e4e4930b27": + return "LightWire"; + + case "Lofted": + case "d381e0f5-3def-4a0d-8853-31e9200bcbda": + return "Lofted"; + + case "Marker": + case "429ed64a-4e97-4466-84d3-145a861ef684": + return "Marker"; + + case "MatteHull": + case "79348357-432d-4746-8e29-0e25c112e3aa": + return "MatteHull"; + + case "NeonPulse": + case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": + return "NeonPulse"; + + case "OilPaint": + case "f72ec0e7-a844-4e38-82e3-140c44772699": + case "c515dad7-4393-4681-81ad-162ef052241b": + return "OilPaint"; + + case "Paper": + case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": + case "759f1ebd-20cd-4720-8d41-234e0da63716": + return "Paper"; + + case "PbrTemplate": + case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": + return "PbrTemplate"; + + case "PbrTransparentTemplate": + case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": + return "PbrTransparentTemplate"; + + case "Petal": + case "e0abbc80-0f80-e854-4970-8924a0863dcc": + return "Petal"; + + case "Plasma": + case "c33714d1-b2f9-412e-bd50-1884c9d46336": + return "Plasma"; + + case "Rainbow": + case "ad1ad437-76e2-450d-a23a-e17f8310b960": + return "Rainbow"; + + case "ShinyHull": + case "faaa4d44-fcfb-4177-96be-753ac0421ba3": + return "ShinyHull"; + + case "Smoke": + case "70d79cca-b159-4f35-990c-f02193947fe8": + return "Smoke"; + + case "Snow": + case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": + return "Snow"; + + case "SoftHighlighter": + case "accb32f5-4509-454f-93f8-1df3fd31df1b": + return "SoftHighlighter"; + + case "Spikes": + case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": + return "Spikes"; + + case "Splatter": + case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": + case "7a1c8107-50c5-4b70-9a39-421576d6617e": + return "Splatter"; + + case "Stars": + case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": + return "Stars"; + + case "Streamers": + case "44bb800a-fbc3-4592-8426-94ecb05ddec3": + return "Streamers"; + + case "Taffy": + case "0077f88c-d93a-42f3-b59b-b31c50cdb414": + return "Taffy"; + + case "TaperedFlat": + case "b468c1fb-f254-41ed-8ec9-57030bc5660c": + case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": + return "TaperedFlat"; + + case "TaperedMarker": + case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": + case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": + return "TaperedMarker"; + + case "ThickPaint": + case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": + case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": + return "ThickPaint"; + + case "Toon": + case "4391385a-df73-4396-9e33-31e4e4930b27": + return "Toon"; + + case "UnlitHull": + case "a8fea537-da7c-4d4b-817f-24f074725d6d": + return "UnlitHull"; + + case "VelvetInk": + case "d229d335-c334-495a-a801-660ac8a87360": + return "VelvetInk"; + + case "Waveform": + case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": + return "Waveform"; + + case "WetPaint": + case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": + case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": + return "WetPaint"; + + case "WigglyGraphite": + case "5347acf0-a8e2-47b6-8346-30c70719d763": + case "e814fef1-97fd-7194-4a2f-50c2bb918be2": + return "WigglyGraphite"; + + case "wire": + case "4391385a-cf83-4396-9e33-31e4e4930b27": + return "Wire"; + } } +} + +const tiltBrushMaterialParams$1 = { + "BlocksBasic" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.2 }, + u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", + fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGem" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, + u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, + u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.9 }, + u_RimIntensity: { value: 0.5 }, + u_RimPower: { value: 2 }, + u_Frequency: { value: 2 }, + u_Jitter: { value: 1 }, + u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, + u_fogDensity: {value: 0 } + }, + vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", + fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGlass" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.8 }, + u_RimIntensity: { value: 0.7 }, + u_RimPower: { value: 4 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl", + fragmentShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Bubbles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png" }, + }, + vertexShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl", + fragmentShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "CelVinyl" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_Cutoff: { value: 0.554 }, + u_MainTex: { value: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl", + fragmentShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "ChromaticWave" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.45 }, + }, + vertexShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl", + fragmentShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201 + }, + "CoarseBristles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.25 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl", + fragmentShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Comet" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png" }, + u_AlphaMask: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png" }, + u_AlphaMask_TexelSize: { value: new Vector4(0.0156, 1, 64, 1)}, + u_time: { value: new Vector4() }, + u_Speed: { value: 1 }, + u_EmissionGain: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl", + fragmentShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "DiamondHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + cameraPosition: { value: new Vector3() }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl", + fragmentShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, blendSrcAlpha: 201, blendSrc: 201, }, @@ -48546,7 +51557,7 @@ class GLTFGoogleTiltBrushMaterialExtension { this.brushPath += "/"; } - this.tiltShaderLoader = new TiltShaderLoader(parser.options.manager); + this.tiltShaderLoader = new TiltShaderLoader$1(parser.options.manager); this.tiltShaderLoader.setPath(brushPath); this.clock = new Clock(); } @@ -48597,26 +51608,25 @@ class GLTFGoogleTiltBrushMaterialExtension { scene.traverse(async object => { const association = parser.associations.get(object); - if (association === undefined || association.type !== "nodes") { - return; - } - - const node = json.nodes[association.index]; - if (node.mesh === undefined) { + if (association === undefined || association.meshes === undefined) { return; } - const prim = json.meshes[node.mesh].primitives[0]; - const mat = json.materials[prim.material]; - const extensionsDef = mat.extensions; - - if (!extensionsDef || !extensionsDef[this.name]) { - return; - } - - const guid = extensionsDef.GOOGLE_tilt_brush_material.guid; + const mesh = json.meshes[association.meshes]; + mesh.primitives.forEach((prim) => { + if(!prim.material) { + return; + } - shaderResolves.push(this.replaceMaterial(object, guid)); + const mat = json.materials[prim.material]; + if (!mat.extensions || !mat.extensions[this.name]) { + return; + } + + const guid = mat.extensions.GOOGLE_tilt_brush_material.guid; + + shaderResolves.push(this.replaceMaterial(object, guid)); + }); }); } @@ -48625,8 +51635,6 @@ class GLTFGoogleTiltBrushMaterialExtension { async replaceMaterial(mesh, guid) { let shader; - let updateTime = false; - let updateCamera = false; switch(guid) { case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": @@ -48710,7 +51718,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_ChromaticWave"; - updateTime = true; break; case "1161af82-50cf-47db-9706-0c3576d43c43": @@ -48740,7 +51747,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Comet"; - updateTime = true; break; case "c8313697-2563-47fc-832e-290f4c04b901": @@ -48755,8 +51761,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_DiamondHull"; - updateTime = true; - updateCamera = true; break; case "4391aaaa-df73-4396-9e33-31e4e4930b27": @@ -48771,7 +51775,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Disco"; - updateTime = true; break; case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": @@ -48857,7 +51860,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader = await this.tiltShaderLoader.loadAsync("Electricity"); mesh.material = shader; mesh.material.name = "material_Electricity"; - updateTime = true; break; case "02ffb866-7fb2-4d15-b761-1012cefb1360": @@ -48871,7 +51873,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader = await this.tiltShaderLoader.loadAsync("Embers"); mesh.material = shader; mesh.material.name = "material_Embers"; - updateTime = true; break; case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": @@ -48914,7 +51915,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Fire"; - updateTime = true; break; case "2d35bcf0-e4d8-452c-97b1-3311be063130": @@ -48960,7 +51960,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Hypercolor"; - updateTime = true; break; case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": @@ -49049,7 +52048,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_LightWire"; - updateTime = true; break; case "d381e0f5-3def-4a0d-8853-31e9200bcbda": @@ -49105,7 +52103,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_NeonPulse"; - updateTime = true; break; case "f72ec0e7-a844-4e38-82e3-140c44772699": @@ -49192,7 +52189,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Plasma"; - updateTime = true; break; case "ad1ad437-76e2-450d-a23a-e17f8310b960": @@ -49207,7 +52203,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Rainbow"; - updateTime = true; break; case "faaa4d44-fcfb-4177-96be-753ac0421ba3": @@ -49249,7 +52244,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader = await this.tiltShaderLoader.loadAsync("Snow"); mesh.material = shader; mesh.material.name = "material_Snow"; - updateTime = true; break; case "accb32f5-4509-454f-93f8-1df3fd31df1b": @@ -49308,7 +52302,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Stars"; - updateTime = true; break; case "44bb800a-fbc3-4592-8426-94ecb05ddec3": @@ -49323,7 +52316,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Streamers"; - updateTime = true; break; case "0077f88c-d93a-42f3-b59b-b31c50cdb414": @@ -49338,7 +52330,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Taffy"; - updateTime = true; break; case "b468c1fb-f254-41ed-8ec9-57030bc5660c": @@ -49436,7 +52427,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_Waveform"; - updateTime = true; break; case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": @@ -49467,7 +52457,6 @@ class GLTFGoogleTiltBrushMaterialExtension { shader.uniformsNeedUpdate = true; mesh.material = shader; mesh.material.name = "material_WigglyGraphite"; - updateTime = true; break; case "4391385a-cf83-4396-9e33-31e4e4930b27": @@ -49485,17 +52474,16 @@ class GLTFGoogleTiltBrushMaterialExtension { } mesh.onBeforeRender = (renderer, scene, camera, geometry, material, group) => { - // Check for any dynamic uniforms set. - if(updateTime || updateCamera) { + if (material.uniforms["u_time"]) { const elapsedTime = this.clock.getElapsedTime(); // _Time from https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html const time = new Vector4(elapsedTime/20, elapsedTime, elapsedTime*2, elapsedTime*3); - if (updateTime) { - material.uniforms["u_time"].value = time; - } - if (updateCamera) { - material.uniforms["cameraPosition"].value = camera.position; - } + + material.uniforms["u_time"].value = time; + } + + if (material.uniforms["cameraPosition"]) { + material.uniforms["cameraPosition"].value = camera.position; } if(material?.uniforms?.directionalLights?.value) { @@ -49930,57 +52918,1644 @@ function strFromU8(dat, latin1) { return out; } } -// skip local zip header -var slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); }; -// read zip header -var zh = function (d, b, z) { - var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20); - var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2]; - return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off]; -}; -// read zip64 extra field -var z64e = function (d, b) { - for (; b2(d, b) != 1; b += 4 + b2(d, b + 2)) - ; - return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)]; +// skip local zip header +var slzh = function (d, b) { return b + 30 + b2(d, b + 26) + b2(d, b + 28); }; +// read zip header +var zh = function (d, b, z) { + var fnl = b2(d, b + 28), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl, bs = b4(d, b + 20); + var _a = z && bs == 4294967295 ? z64e(d, es) : [bs, b4(d, b + 24), b4(d, b + 42)], sc = _a[0], su = _a[1], off = _a[2]; + return [b2(d, b + 10), sc, su, fn, es + b2(d, b + 30) + b2(d, b + 32), off]; +}; +// read zip64 extra field +var z64e = function (d, b) { + for (; b2(d, b) != 1; b += 4 + b2(d, b + 2)) + ; + return [b8(d, b + 12), b8(d, b + 4), b8(d, b + 20)]; +}; +/** + * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better + * performance with more than one file. + * @param data The raw compressed ZIP file + * @returns The decompressed files + */ +function unzipSync(data) { + var files = {}; + var e = data.length - 22; + for (; b4(data, e) != 0x6054B50; --e) { + if (!e || data.length - e > 65558) + throw 'invalid zip file'; + } + var c = b2(data, e + 8); + if (!c) + return {}; + var o = b4(data, e + 16); + var z = o == 4294967295; + if (z) { + e = b4(data, e - 12); + if (b4(data, e) != 0x6064B50) + throw 'invalid zip file'; + c = b4(data, e + 32); + o = b4(data, e + 48); + } + for (var i = 0; i < c; ++i) { + var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off); + o = no; + if (!c_2) + files[fn] = slc(data, b, b + sc); + else if (c_2 == 8) + files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su)); + else + throw 'unknown compression type ' + c_2; + } + return files; +} + +/*! + * three-icosa + * https://github.com/icosa-gallery/three-icosa + * Copyright (c) 2021-2022 Icosa Gallery + * Released under the Apache 2.0 Licence. + */ + +// Copyright 2021-2022 Icosa Gallery + +class TiltShaderLoader extends Loader { + constructor( manager ) { + super( manager ); + + this.loadedMaterials = {}; + } + + async load(brushName, onLoad, onProgress, onError ) { + const scope = this; + + const isAlreadyLoaded = this.loadedMaterials[brushName]; + + if (isAlreadyLoaded !== undefined) { + onLoad( scope.parse( isAlreadyLoaded ) ); + return; + } + + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'text' ); + loader.setWithCredentials( this.withCredentials ); + + const textureLoader = new TextureLoader(this.manager); + textureLoader.setPath(this.path); + textureLoader.setWithCredentials( this.withCredentials ); + + const materialParams = tiltBrushMaterialParams[brushName]; + + materialParams.vertexShader = await loader.loadAsync(materialParams.vertexShader); + materialParams.fragmentShader = await loader.loadAsync(materialParams.fragmentShader); + + if (materialParams.uniforms.u_MainTex) { + const mainTex = await textureLoader.loadAsync(materialParams.uniforms.u_MainTex.value); + mainTex.name = `${brushName}_MainTex`; + mainTex.wrapS = RepeatWrapping; + mainTex.wrapT = RepeatWrapping; + mainTex.flipY = false; + materialParams.uniforms.u_MainTex.value = mainTex; + } + + if (materialParams.uniforms.u_BumpMap) { + const bumpMap = await textureLoader.loadAsync(materialParams.uniforms.u_BumpMap.value); + bumpMap.name = `${brushName}_BumpMap`; + bumpMap.wrapS = RepeatWrapping; + bumpMap.wrapT = RepeatWrapping; + bumpMap.flipY = false; + materialParams.uniforms.u_BumpMap.value = bumpMap; + } + + if (materialParams.uniforms.u_AlphaMask) { + const alphaMask = await textureLoader.loadAsync(materialParams.uniforms.u_AlphaMask.value); + alphaMask.name = `${brushName}_AlphaMask`; + alphaMask.wrapS = RepeatWrapping; + alphaMask.wrapT = RepeatWrapping; + alphaMask.flipY = false; + materialParams.uniforms.u_AlphaMask.value = alphaMask; + } + + // inject three.js lighting uniforms + for(var lightType in UniformsLib.lights) + { + materialParams.uniforms[lightType] = UniformsLib.lights[lightType]; + } + + let rawMaterial = new RawShaderMaterial(materialParams); + this.loadedMaterials[brushName] = rawMaterial; + onLoad( scope.parse( rawMaterial ) ); + } + + parse( rawMaterial ) { + return rawMaterial; + } + + lookupMaterial(nameOrGuid) { + const name = this.lookupMaterialName(nameOrGuid); + return tiltBrushMaterialParams[name]; + } + + lookupMaterialName(nameOrGuid) { + switch(nameOrGuid) { + case "BlocksBasic:": + case "0e87b49c-6546-3a34-3a44-8a556d7d6c3e": + return "BlocksBasic"; + + case "BlocksGem": + case "232998f8-d357-47a2-993a-53415df9be10": + return "BlocksGem"; + + case "BlocksGlass": + case "3d813d82-5839-4450-8ddc-8e889ecd96c7": + return "BlocksGlass"; + + case "Bubbles": + case "89d104cd-d012-426b-b5b3-bbaee63ac43c": + return "Bubbles"; + + case "CelVinyl": + case "700f3aa8-9a7c-2384-8b8a-ea028905dd8c": + return "CelVinyl"; + + case "ChromaticWave": + case "0f0ff7b2-a677-45eb-a7d6-0cd7206f4816": + return "ChromaticWave"; + + case "CoarseBristles": + case "1161af82-50cf-47db-9706-0c3576d43c43": + case "79168f10-6961-464a-8be1-57ed364c5600": + return "CoarseBristles"; + + case "Comet": + case "1caa6d7d-f015-3f54-3a4b-8b5354d39f81": + return "Comet"; + + case "DiamondHull": + case "c8313697-2563-47fc-832e-290f4c04b901": + return "DiamondHull"; + + case "Disco": + case "4391aaaa-df73-4396-9e33-31e4e4930b27": + return "Disco"; + + case "DotMarker": + case "d1d991f2-e7a0-4cf1-b328-f57e915e6260": + return "DotMarker"; + + case "Dots": + case "6a1cf9f9-032c-45ec-9b1d-a6680bee30f7": + return "Dots"; + + case "DoubleTaperedFlat": + case "0d3889f3-3ede-470c-8af4-f44813306126": + return "DoubleTaperedFlat"; + + case "DoubleTaperedMarker": + case "0d3889f3-3ede-470c-8af4-de4813306126": + return "DoubleTaperedMarker"; + + case "DuctTape": + case "d0262945-853c-4481-9cbd-88586bed93cb": + case "3ca16e2f-bdcd-4da2-8631-dcef342f40f1": + return "DuctTape"; + + case "Electricity": + case "f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51": + return "Electricity"; + + case "Embers": + case "02ffb866-7fb2-4d15-b761-1012cefb1360": + return "Embers"; + + case "EnvironmentDiffuse": + case "0ad58bbd-42bc-484e-ad9a-b61036ff4ce7": + return "EnvironmentDiffuse"; + + case "EnvironmentDiffuseLightMap": + case "d01d9d6c-9a61-4aba-8146-5891fafb013b": + return "EnvironmentDiffuseLightMap"; + + case "Fire": + case "cb92b597-94ca-4255-b017-0e3f42f12f9e": + return "Fire"; + + case "2d35bcf0-e4d8-452c-97b1-3311be063130": + case "280c0a7a-aad8-416c-a7d2-df63d129ca70": + case "55303bc4-c749-4a72-98d9-d23e68e76e18": + case "Flat": + return "Flat"; + + case "cf019139-d41c-4eb0-a1d0-5cf54b0a42f3": + case "geometry_Highlighter": + return "Highlighter"; + + case "Hypercolor": + case "dce872c2-7b49-4684-b59b-c45387949c5c": + case "e8ef32b1-baa8-460a-9c2c-9cf8506794f5": + return "Hypercolor"; + + case "HyperGrid": + case "6a1cf9f9-032c-45ec-9b6e-a6680bee32e9": + return "HyperGrid"; + + case "Icing": + case "2f212815-f4d3-c1a4-681a-feeaf9c6dc37": + return "Icing"; + + case "Ink": + case "f5c336cf-5108-4b40-ade9-c687504385ab": + case "c0012095-3ffd-4040-8ee1-fc180d346eaa": + return "Ink"; + + case "Leaves": + case "4a76a27a-44d8-4bfe-9a8c-713749a499b0": + case "ea19de07-d0c0-4484-9198-18489a3c1487": + return "Leaves"; + + case "Light": + case "2241cd32-8ba2-48a5-9ee7-2caef7e9ed62": + return "Light"; + + case "LightWire": + case "4391aaaa-df81-4396-9e33-31e4e4930b27": + return "LightWire"; + + case "Lofted": + case "d381e0f5-3def-4a0d-8853-31e9200bcbda": + return "Lofted"; + + case "Marker": + case "429ed64a-4e97-4466-84d3-145a861ef684": + return "Marker"; + + case "MatteHull": + case "79348357-432d-4746-8e29-0e25c112e3aa": + return "MatteHull"; + + case "NeonPulse": + case "b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6": + return "NeonPulse"; + + case "OilPaint": + case "f72ec0e7-a844-4e38-82e3-140c44772699": + case "c515dad7-4393-4681-81ad-162ef052241b": + return "OilPaint"; + + case "Paper": + case "f1114e2e-eb8d-4fde-915a-6e653b54e9f5": + case "759f1ebd-20cd-4720-8d41-234e0da63716": + return "Paper"; + + case "PbrTemplate": + case "f86a096c-2f4f-4f9d-ae19-81b99f2944e0": + return "PbrTemplate"; + + case "PbrTransparentTemplate": + case "19826f62-42ac-4a9e-8b77-4231fbd0cfbf": + return "PbrTransparentTemplate"; + + case "Petal": + case "e0abbc80-0f80-e854-4970-8924a0863dcc": + return "Petal"; + + case "Plasma": + case "c33714d1-b2f9-412e-bd50-1884c9d46336": + return "Plasma"; + + case "Rainbow": + case "ad1ad437-76e2-450d-a23a-e17f8310b960": + return "Rainbow"; + + case "ShinyHull": + case "faaa4d44-fcfb-4177-96be-753ac0421ba3": + return "ShinyHull"; + + case "Smoke": + case "70d79cca-b159-4f35-990c-f02193947fe8": + return "Smoke"; + + case "Snow": + case "d902ed8b-d0d1-476c-a8de-878a79e3a34c": + return "Snow"; + + case "SoftHighlighter": + case "accb32f5-4509-454f-93f8-1df3fd31df1b": + return "SoftHighlighter"; + + case "Spikes": + case "cf7f0059-7aeb-53a4-2b67-c83d863a9ffa": + return "Spikes"; + + case "Splatter": + case "8dc4a70c-d558-4efd-a5ed-d4e860f40dc3": + case "7a1c8107-50c5-4b70-9a39-421576d6617e": + return "Splatter"; + + case "Stars": + case "0eb4db27-3f82-408d-b5a1-19ebd7d5b711": + return "Stars"; + + case "Streamers": + case "44bb800a-fbc3-4592-8426-94ecb05ddec3": + return "Streamers"; + + case "Taffy": + case "0077f88c-d93a-42f3-b59b-b31c50cdb414": + return "Taffy"; + + case "TaperedFlat": + case "b468c1fb-f254-41ed-8ec9-57030bc5660c": + case "c8ccb53d-ae13-45ef-8afb-b730d81394eb": + return "TaperedFlat"; + + case "TaperedMarker": + case "d90c6ad8-af0f-4b54-b422-e0f92abe1b3c": + case "1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0": + return "TaperedMarker"; + + case "ThickPaint": + case "75b32cf0-fdd6-4d89-a64b-e2a00b247b0f": + case "fdf0326a-c0d1-4fed-b101-9db0ff6d071f": + return "ThickPaint"; + + case "Toon": + case "4391385a-df73-4396-9e33-31e4e4930b27": + return "Toon"; + + case "UnlitHull": + case "a8fea537-da7c-4d4b-817f-24f074725d6d": + return "UnlitHull"; + + case "VelvetInk": + case "d229d335-c334-495a-a801-660ac8a87360": + return "VelvetInk"; + + case "Waveform": + case "10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab": + return "Waveform"; + + case "WetPaint": + case "b67c0e81-ce6d-40a8-aeb0-ef036b081aa3": + case "dea67637-cd1a-27e4-c9b1-52f4bbcb84e5": + return "WetPaint"; + + case "WigglyGraphite": + case "5347acf0-a8e2-47b6-8346-30c70719d763": + case "e814fef1-97fd-7194-4a2f-50c2bb918be2": + return "WigglyGraphite"; + + case "wire": + case "4391385a-cf83-4396-9e33-31e4e4930b27": + return "Wire"; + } } +} + +const tiltBrushMaterialParams = { + "BlocksBasic" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.2 }, + u_SpecColor: { value: new Vector3(0.1960784, 0.1960784, 0.1960784) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-vertex.glsl", + fragmentShader: "BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e/BlocksBasic-0e87b49c-6546-3a34-3a44-8a556d7d6c3e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGem" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: {value: new Vector4(0.3922, 0.3922, 0.3922, 1)}, + u_SceneLight_0_color: {value: new Vector4(0.7780, 0.8157, 0.9914, 1)}, + u_SceneLight_1_color: {value: new Vector4(0.4282, 0.4212, 0.3459, 1)}, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.9 }, + u_RimIntensity: { value: 0.5 }, + u_RimPower: { value: 2 }, + u_Frequency: { value: 2 }, + u_Jitter: { value: 1 }, + u_fogColor: {value: new Vector3(0.0196, 0.0196, 0.0196)}, + u_fogDensity: {value: 0 } + }, + vertexShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-vertex.glsl", + fragmentShader: "BlocksGem-232998f8-d357-47a2-993a-53415df9be10/BlocksGem-232998f8-d357-47a2-993a-53415df9be10-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "BlocksGlass" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Color: { value: new Vector4(1, 1, 1, 1) }, + u_Shininess: { value: 0.8 }, + u_RimIntensity: { value: 0.7 }, + u_RimPower: { value: 4 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-vertex.glsl", + fragmentShader: "BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7/BlocksGlass-3d813d82-5839-4450-8ddc-8e889ecd96c7-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Bubbles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-MainTex.png" }, + }, + vertexShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-vertex.glsl", + fragmentShader: "Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c/Bubbles-89d104cd-d012-426b-b5b3-bbaee63ac43c-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "CelVinyl" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_Cutoff: { value: 0.554 }, + u_MainTex: { value: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-vertex.glsl", + fragmentShader: "CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c/CelVinyl-700f3aa8-9a7c-2384-8b8a-ea028905dd8c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "ChromaticWave" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.45 }, + }, + vertexShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-vertex.glsl", + fragmentShader: "ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816/ChromaticWave-0f0ff7b2-a677-45eb-a7d6-0cd7206f4816-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201 + }, + "CoarseBristles" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.25 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-vertex.glsl", + fragmentShader: "CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43/CoarseBristles-1161af82-50cf-47db-9706-0c3576d43c43-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Comet" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-MainTex.png" }, + u_AlphaMask: { value: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-AlphaMask.png" }, + u_AlphaMask_TexelSize: { value: new Vector4(0.0156, 1, 64, 1)}, + u_time: { value: new Vector4() }, + u_Speed: { value: 1 }, + u_EmissionGain: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-vertex.glsl", + fragmentShader: "Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81/Comet-1caa6d7d-f015-3f54-3a4b-8b5354d39f81-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "DiamondHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + cameraPosition: { value: new Vector3() }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-vertex.glsl", + fragmentShader: "DiamondHull-c8313697-2563-47fc-832e-290f4c04b901/DiamondHull-c8313697-2563-47fc-832e-290f4c04b901-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Disco" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.65 }, + u_SpecColor: { value: new Vector3(0.5147059, 0.5147059, 0.5147059) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Disco-4391aaaa-df73-4396-9e33-31e4e4930b27/Disco-4391aaaa-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DotMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-vertex.glsl", + fragmentShader: "DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260/DotMarker-d1d991f2-e7a0-4cf1-b328-f57e915e6260-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + + }, + "Dots" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-MainTex.png" }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_EmissionGain: { value: 300 }, + u_BaseGain: { value: 0.4 } + }, + vertexShader: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-vertex.glsl", + fragmentShader: "Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7/Dots-6a1cf9f9-032c-45ec-9b1d-a6680bee30f7-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "DoubleTaperedFlat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.1500 }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-vertex.glsl", + fragmentShader: "DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126/DoubleTaperedFlat-0d3889f3-3ede-470c-8af4-f44813306126-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DoubleTaperedMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-vertex.glsl", + fragmentShader: "DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126/DoubleTaperedMarker-0d3889f3-3ede-470c-8af4-de4813306126-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "DuctTape" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.5372549, 0.5372549, 0.5372549) }, + u_Shininess: { value: 0.414 }, + u_MainTex: { value: "DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1/DuctTape-3ca16e2f-bdcd-4da2-8631-dcef342f40f1-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-vertex.glsl", + fragmentShader: "DuctTape-d0262945-853c-4481-9cbd-88586bed93cb/DuctTape-d0262945-853c-4481-9cbd-88586bed93cb-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Electricity" : { + uniforms: { + u_time: { value: new Vector4() }, + u_DisplacementIntensity: { value: 2.0 }, + u_EmissionGain: { value: 0.2 } + }, + vertexShader: "Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-vertex.glsl", + fragmentShader: "Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51/Electricity-f6e85de3-6dcc-4e7f-87fd-cee8c3d25d51-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Embers" : { + uniforms: { + u_time: { value: new Vector4() }, + u_ScrollRate: { value: 0.6 }, + u_ScrollDistance: { value: new Vector3(-0.2, 0.6, 0) }, + u_ScrollJitterIntensity: { value: 0.03 }, + u_ScrollJitterFrequency: { value: 5 }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-vertex.glsl", + fragmentShader: "Embers-02ffb866-7fb2-4d15-b761-1012cefb1360/Embers-02ffb866-7fb2-4d15-b761-1012cefb1360-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "EnvironmentDiffuse" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-vertex.glsl", + fragmentShader: "EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7/EnvironmentDiffuse-0ad58bbd-42bc-484e-ad9a-b61036ff4ce7-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "EnvironmentDiffuseLightMap" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-vertex.glsl", + fragmentShader: "EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b/EnvironmentDiffuseLightMap-d01d9d6c-9a61-4aba-8146-5891fafb013b-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Fire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.5 } + }, + vertexShader: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-vertex.glsl", + fragmentShader: "Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e/Fire-cb92b597-94ca-4255-b017-0e3f42f12f9e-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201 + }, + "Flat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-vertex.glsl", + fragmentShader: "Flat-2d35bcf0-e4d8-452c-97b1-3311be063130/Flat-2d35bcf0-e4d8-452c-97b1-3311be063130-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Highlighter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.12 } + }, + vertexShader: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-vertex.glsl", + fragmentShader: "Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3/Highlighter-cf019139-d41c-4eb0-a1d0-5cf54b0a42f3-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Hypercolor" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.5 }, + u_SpecColor: { value: new Vector3(0.2745098, 0.2745098, 0.2745098) }, + u_MainTex: { value: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-MainTex.png" }, + u_time: { value: new Vector4() }, + u_Cutoff: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-vertex.glsl", + fragmentShader: "Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c/Hypercolor-dce872c2-7b49-4684-b59b-c45387949c5c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "HyperGrid" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-MainTex.png" } + }, + vertexShader: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-vertex.glsl", + fragmentShader: "HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9/HyperGrid-6a1cf9f9-032c-45ec-9b6e-a6680bee32e9-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Icing" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.1500 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-vertex.glsl", + fragmentShader: "Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37/Icing-2f212815-f4d3-c1a4-681a-feeaf9c6dc37-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Ink" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa/Ink-c0012095-3ffd-4040-8ee1-fc180d346eaa-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-vertex.glsl", + fragmentShader: "Ink-f5c336cf-5108-4b40-ade9-c687504385ab/Ink-f5c336cf-5108-4b40-ade9-c687504385ab-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Leaves" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.395 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-vertex.glsl", + fragmentShader: "Leaves-ea19de07-d0c0-4484-9198-18489a3c1487/Leaves-ea19de07-d0c0-4484-9198-18489a3c1487-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Light" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-MainTex.png" }, + u_EmissionGain: { value: 0.45 }, + }, + vertexShader: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-vertex.glsl", + fragmentShader: "Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62/Light-2241cd32-8ba2-48a5-9ee7-2caef7e9ed62-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "LightWire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Shininess: { value: 0.81 }, + u_SpecColor: { value: new Vector3(0.3455882, 0.3455882, 0.3455882) }, + u_time: { value: new Vector4() }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_MainTex: { value: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-MainTex.png"} + }, + vertexShader: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27/LightWire-4391aaaa-df81-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Lofted" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-vertex.glsl", + fragmentShader: "Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda/Lofted-d381e0f5-3def-4a0d-8853-31e9200bcbda-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Marker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.067 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-vertex.glsl", + fragmentShader: "Marker-429ed64a-4e97-4466-84d3-145a861ef684/Marker-429ed64a-4e97-4466-84d3-145a861ef684-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + + }, + "MatteHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-vertex.glsl", + fragmentShader: "MatteHull-79348357-432d-4746-8e29-0e25c112e3aa/MatteHull-79348357-432d-4746-8e29-0e25c112e3aa-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "NeonPulse" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.5 }, + }, + vertexShader: "NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-vertex.glsl", + fragmentShader: "NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6/NeonPulse-b2ffef01-eaaa-4ab5-aa64-95a2c4f5dbc6-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "OilPaint": { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0020, 0.0020, 512, 512) }, + }, + vertexShader: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-vertex.glsl", + fragmentShader: "OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699/OilPaint-f72ec0e7-a844-4e38-82e3-140c44772699-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Paper" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.145 }, + u_Cutoff: { value: 0.16 }, + u_MainTex: { value: "Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "Paper-759f1ebd-20cd-4720-8d41-234e0da63716/Paper-759f1ebd-20cd-4720-8d41-234e0da63716-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-vertex.glsl", + fragmentShader: "Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5/Paper-f1114e2e-eb8d-4fde-915a-6e653b54e9f5-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "PbrTemplate" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-vertex.glsl", + fragmentShader: "PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0/PbrTemplate-f86a096c-2f4f-4f9d-ae19-81b99f2944e0-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "PbrTransparentTemplate" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-vertex.glsl", + fragmentShader: "PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf/PbrTransparentTemplate-19826f62-42ac-4a9e-8b77-4231fbd0cfbf-v1.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Petal" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.01 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-vertex.glsl", + fragmentShader: "Petal-e0abbc80-0f80-e854-4970-8924a0863dcc/Petal-e0abbc80-0f80-e854-4970-8924a0863dcc-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + // How did an experimental brush end up here? + "Plasma" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-MainTex.png" }, + u_time: { value: new Vector4() } + }, + vertexShader: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-vertex.glsl", + fragmentShader: "Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336/Plasma-c33714d1-b2f9-412e-bd50-1884c9d46336-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Rainbow" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_EmissionGain: { value: 0.65 } + }, + vertexShader: "Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-vertex.glsl", + fragmentShader: "Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960/Rainbow-ad1ad437-76e2-450d-a23a-e17f8310b960-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "ShinyHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.1985294, 0.1985294, 0.1985294) }, + u_Shininess: { value: 0.7430 }, + u_Cutoff: { value: 0.5 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-vertex.glsl", + fragmentShader: "ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3/ShinyHull-faaa4d44-fcfb-4177-96be-753ac0421ba3-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Smoke": { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-MainTex.png" } + }, + vertexShader: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-vertex.glsl", + fragmentShader: "Smoke-70d79cca-b159-4f35-990c-f02193947fe8/Smoke-70d79cca-b159-4f35-990c-f02193947fe8-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Snow" : { + uniforms: { + u_time: { value: new Vector4() }, + u_ScrollRate: { value: 0.2 }, + u_ScrollDistance: { value: new Vector3(0, -0.3, 0) }, + u_ScrollJitterIntensity: { value: 0.01 }, + u_ScrollJitterFrequency: { value: 12 }, + u_TintColor: { value: new Vector4(1, 1, 1, 1) }, + u_MainTex: { value: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-MainTex.png" }, + }, + vertexShader: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-vertex.glsl", + fragmentShader: "Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c/Snow-d902ed8b-d0d1-476c-a8de-878a79e3a34c-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "SoftHighlighter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-MainTex.png" }, + }, + vertexShader: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-vertex.glsl", + fragmentShader: "SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b/SoftHighlighter-accb32f5-4509-454f-93f8-1df3fd31df1b-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 5, + blendDstAlpha: 201, + blendDst: 201, + blendEquationAlpha: 100, + blendEquation: 100, + blendSrcAlpha: 201, + blendSrc: 201, + }, + "Spikes" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-vertex.glsl", + fragmentShader: "Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa/Spikes-cf7f0059-7aeb-53a4-2b67-c83d863a9ffa-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Splatter" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-vertex.glsl", + fragmentShader: "Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e/Splatter-7a1c8107-50c5-4b70-9a39-421576d6617e-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Stars" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_SparkleRate: { value: 5.3 }, + u_MainTex: { value: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-MainTex.png" }, + }, + vertexShader: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-vertex.glsl", + fragmentShader: "Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711/Stars-0eb4db27-3f82-408d-b5a1-19ebd7d5b711-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Streamers" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-MainTex.png" }, + u_EmissionGain: { value: 0.4 }, + u_time: { value: new Vector4() }, + }, + vertexShader: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-vertex.glsl", + fragmentShader: "Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3/Streamers-44bb800a-fbc3-4592-8426-94ecb05ddec3-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Taffy" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-vertex.glsl", + fragmentShader: "Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414/Taffy-0077f88c-d93a-42f3-b59b-b31c50cdb414-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedFlat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_MainTex: { value: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.067 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-vertex.glsl", + fragmentShader: "TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c/TaperedFlat-b468c1fb-f254-41ed-8ec9-57030bc5660c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedMarker" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-vertex.glsl", + fragmentShader: "TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c/TaperedMarker-d90c6ad8-af0f-4b54-b422-e0f92abe1b3c-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "TaperedMarker_Flat" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0, 0, 0) }, + u_Shininess: { value: 0.1500 }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_MainTex: { value: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-MainTex.png" }, + u_Cutoff: { value: 0.2 } + }, + vertexShader: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-vertex.glsl", + fragmentShader: "TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0/TaperedMarker_Flat-1a26b8c0-8a07-4f8a-9fac-d2ef36e0cad0-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "ThickPaint" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.2352941, 0.2352941, 0.2352941) }, + u_Shininess: { value: 0.4 }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-vertex.glsl", + fragmentShader: "ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f/ThickPaint-75b32cf0-fdd6-4d89-a64b-e2a00b247b0f-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Toon" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Toon-4391385a-df73-4396-9e33-31e4e4930b27/Toon-4391385a-df73-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "UnlitHull" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-vertex.glsl", + fragmentShader: "UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d/UnlitHull-a8fea537-da7c-4d4b-817f-24f074725d6d-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, + "VelvetInk" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_MainTex: { value: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-vertex.glsl", + fragmentShader: "VelvetInk-d229d335-c334-495a-a801-660ac8a87360/VelvetInk-d229d335-c334-495a-a801-660ac8a87360-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "Waveform" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_EmissionGain: { value: 0.5178571 }, + u_time: { value: new Vector4() }, + u_MainTex: { value: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-MainTex.png" }, + }, + vertexShader: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-vertex.glsl", + fragmentShader: "Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab/Waveform-10201aa3-ebc2-42d8-84b7-2e63f6eeb8ab-v10.0-fragment.glsl", + side: 2, + transparent: true, + depthFunc: 2, + depthWrite: false, + depthTest: true, + blending: 2 + }, + "WetPaint" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_SpecColor: { value: new Vector3(0.1397059, 0.1397059, 0.1397059) }, + u_Shininess: { value: 0.85 }, + u_Cutoff: { value: 0.3 }, + u_MainTex: { value: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + u_BumpMap: { value: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-BumpMap.png" }, + u_BumpMap_TexelSize: { value: new Vector4(0.0010, 0.0078, 1024, 128) }, + }, + vertexShader: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-vertex.glsl", + fragmentShader: "WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3/WetPaint-b67c0e81-ce6d-40a8-aeb0-ef036b081aa3-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "WigglyGraphite" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_time: { value: new Vector4() }, + u_ambient_light_color: { value: new Vector4(0.3922, 0.3922, 0.3922, 1) }, + u_SceneLight_0_color: { value: new Vector4(0.7780, 0.8157, 0.9914, 1) }, + u_SceneLight_1_color: { value: new Vector4(0.4282, 0.4212, 0.3459, 1) }, + u_Cutoff: { value: 0.5 }, + u_MainTex: { value: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-MainTex.png" }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 }, + }, + vertexShader: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-vertex.glsl", + fragmentShader: "WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763/WigglyGraphite-5347acf0-a8e2-47b6-8346-30c70719d763-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0 + }, + "Wire" : { + uniforms: { + u_SceneLight_0_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_SceneLight_1_matrix: { value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] }, + u_fogColor: { value: new Vector3(0.0196, 0.0196, 0.0196) }, + u_fogDensity: { value: 0 } + }, + vertexShader: "Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-vertex.glsl", + fragmentShader: "Wire-4391385a-cf83-4396-9e33-31e4e4930b27/Wire-4391385a-cf83-4396-9e33-31e4e4930b27-v10.0-fragment.glsl", + side: 2, + transparent: false, + depthFunc: 2, + depthWrite: true, + depthTest: true, + blending: 0, + }, }; -/** - * Synchronously decompresses a ZIP archive. Prefer using `unzip` for better - * performance with more than one file. - * @param data The raw compressed ZIP file - * @returns The decompressed files - */ -function unzipSync(data) { - var files = {}; - var e = data.length - 22; - for (; b4(data, e) != 0x6054B50; --e) { - if (!e || data.length - e > 65558) - throw 'invalid zip file'; - } - var c = b2(data, e + 8); - if (!c) - return {}; - var o = b4(data, e + 16); - var z = o == 4294967295; - if (z) { - e = b4(data, e - 12); - if (b4(data, e) != 0x6064B50) - throw 'invalid zip file'; - c = b4(data, e + 32); - o = b4(data, e + 48); - } - for (var i = 0; i < c; ++i) { - var _a = zh(data, o, z), c_2 = _a[0], sc = _a[1], su = _a[2], fn = _a[3], no = _a[4], off = _a[5], b = slzh(data, off); - o = no; - if (!c_2) - files[fn] = slc(data, b, b + sc); - else if (c_2 == 8) - files[fn] = inflateSync(data.subarray(b, b + sc), new u8(su)); - else - throw 'unknown compression type ' + c_2; - } - return files; -} /*! * three-tiltloader @@ -50964,8 +55539,7 @@ var WEBGL_TEXTURE_FORMATS = { var WEBGL_TEXTURE_DATATYPES = { 5121: UnsignedByteType, 32819: UnsignedShort4444Type, - 32820: UnsignedShort5551Type, - 33635: UnsignedShort565Type + 32820: UnsignedShort5551Type }; var WEBGL_SIDES = { @@ -52709,7 +57283,7 @@ class GLTFParser { function replaceBrushMaterials(brushPath, model) { return __awaiter(this, void 0, void 0, function* () { - const tiltShaderLoader = new TiltShaderLoader(DefaultLoadingManager); + const tiltShaderLoader = new TiltShaderLoader$1(DefaultLoadingManager); tiltShaderLoader.setPath(brushPath); const clock = new Clock(); model.traverse((object) => __awaiter(this, void 0, void 0, function* () { diff --git a/package-lock.json b/package-lock.json index 71e5e6d..ac349e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,9 +13,9 @@ "camera-controls": "^1.34.2", "hold-event": "0.0.1", "rollup-plugin-dts": "^4.2.1", - "three": "^0.128.0", - "three-icosa": "^0.3.6", - "three-tiltloader": "^0.4.0" + "three": "^0.139.2", + "three-icosa": "^0.4.0", + "three-tiltloader": "^0.4.1" }, "devDependencies": { "@types/three": "^0.139.0", @@ -3640,26 +3640,26 @@ } }, "node_modules/three": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.128.0.tgz", - "integrity": "sha512-i0ap/E+OaSfzw7bD1TtYnPo3VEplkl70WX5fZqZnfZsE3k3aSFudqrrC9ldFZfYFkn1zwDmBcdGfiIm/hnbyZA==" + "version": "0.139.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.139.2.tgz", + "integrity": "sha512-gV7q7QY8rogu7HLFZR9cWnOQAUedUhu2WXAnpr2kdXZP9YDKsG/0ychwQvWkZN5PlNw9mv5MoCTin6zNTXoONg==" }, "node_modules/three-icosa": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.3.6.tgz", - "integrity": "sha512-rB8676VIxC4k3z5jAvjwxC5Da9/y/g4stC6JB+KuUO0QttSkkHif+FOJdlkq9UweDoB2vvMMY/7TyWAoHmO2QQ==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.4.0.tgz", + "integrity": "sha512-0klokubyxGwW27hP3iq3VyTUVyNIdFYQWcvOnwNg8bleFYLgN1+lYvHTeYFxoJABBu9BXWSd1m1y18x/+OT31g==", "funding": { "type": "opencollective", "url": "https://opencollective.com/icosa" }, "peerDependencies": { - "three": ">=0.128.0" + "three": ">=0.139.0" } }, "node_modules/three-tiltloader": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/three-tiltloader/-/three-tiltloader-0.4.0.tgz", - "integrity": "sha512-CceVQk+3B+/vgHzo6vG4JIGk4Xd1I7W+8Lq0wfQi4pdee4nHvKesGfj45hE4oJYCy/XrTx9D06AsEzaI81Ok1g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/three-tiltloader/-/three-tiltloader-0.4.1.tgz", + "integrity": "sha512-xvG24ssKRac2tR0ws1ksqvmzwk75tC2Ex9zhSQxoOMNH5Ik6M+W/gjtXfbnhkUQNgJLuV2t9cfC+6SlvTjxZGg==", "dependencies": { "three-icosa": "^0.3.2" }, @@ -3668,7 +3668,19 @@ "url": "https://opencollective.com/icosa" }, "peerDependencies": { - "three": "^0.128.0" + "three": "^0.139.0" + } + }, + "node_modules/three-tiltloader/node_modules/three-icosa": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.3.6.tgz", + "integrity": "sha512-rB8676VIxC4k3z5jAvjwxC5Da9/y/g4stC6JB+KuUO0QttSkkHif+FOJdlkq9UweDoB2vvMMY/7TyWAoHmO2QQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/icosa" + }, + "peerDependencies": { + "three": ">=0.128.0" } }, "node_modules/timsort": { @@ -6832,22 +6844,30 @@ } }, "three": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.128.0.tgz", - "integrity": "sha512-i0ap/E+OaSfzw7bD1TtYnPo3VEplkl70WX5fZqZnfZsE3k3aSFudqrrC9ldFZfYFkn1zwDmBcdGfiIm/hnbyZA==" + "version": "0.139.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.139.2.tgz", + "integrity": "sha512-gV7q7QY8rogu7HLFZR9cWnOQAUedUhu2WXAnpr2kdXZP9YDKsG/0ychwQvWkZN5PlNw9mv5MoCTin6zNTXoONg==" }, "three-icosa": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.3.6.tgz", - "integrity": "sha512-rB8676VIxC4k3z5jAvjwxC5Da9/y/g4stC6JB+KuUO0QttSkkHif+FOJdlkq9UweDoB2vvMMY/7TyWAoHmO2QQ==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.4.0.tgz", + "integrity": "sha512-0klokubyxGwW27hP3iq3VyTUVyNIdFYQWcvOnwNg8bleFYLgN1+lYvHTeYFxoJABBu9BXWSd1m1y18x/+OT31g==", "requires": {} }, "three-tiltloader": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/three-tiltloader/-/three-tiltloader-0.4.0.tgz", - "integrity": "sha512-CceVQk+3B+/vgHzo6vG4JIGk4Xd1I7W+8Lq0wfQi4pdee4nHvKesGfj45hE4oJYCy/XrTx9D06AsEzaI81Ok1g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/three-tiltloader/-/three-tiltloader-0.4.1.tgz", + "integrity": "sha512-xvG24ssKRac2tR0ws1ksqvmzwk75tC2Ex9zhSQxoOMNH5Ik6M+W/gjtXfbnhkUQNgJLuV2t9cfC+6SlvTjxZGg==", "requires": { "three-icosa": "^0.3.2" + }, + "dependencies": { + "three-icosa": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/three-icosa/-/three-icosa-0.3.6.tgz", + "integrity": "sha512-rB8676VIxC4k3z5jAvjwxC5Da9/y/g4stC6JB+KuUO0QttSkkHif+FOJdlkq9UweDoB2vvMMY/7TyWAoHmO2QQ==", + "requires": {} + } } }, "timsort": { diff --git a/package.json b/package.json index 96edaa9..cfbc1c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "icosa-viewer", - "version": "0.7.0", + "version": "0.7.1", "description": "Viewer for Tilt Brush / Open Brush files and derivatives", "scripts": { "dev": "rollup --config --watch", @@ -36,9 +36,9 @@ "camera-controls": "^1.34.2", "hold-event": "0.0.1", "rollup-plugin-dts": "^4.2.1", - "three": "^0.128.0", - "three-icosa": "^0.3.6", - "three-tiltloader": "^0.4.0" + "three": "^0.139.2", + "three-icosa": "^0.4.0", + "three-tiltloader": "^0.4.1" }, "files": [ "dist", diff --git a/src/legacy/LegacyGLTFLoader.js b/src/legacy/LegacyGLTFLoader.js index 8add4a4..c0f1b5e 100644 --- a/src/legacy/LegacyGLTFLoader.js +++ b/src/legacy/LegacyGLTFLoader.js @@ -52,7 +52,6 @@ import { UnsignedByteType, UnsignedShort4444Type, UnsignedShort5551Type, - UnsignedShort565Type, Mesh, LineLoop, Line, @@ -508,8 +507,7 @@ var WEBGL_TEXTURE_FORMATS = { var WEBGL_TEXTURE_DATATYPES = { 5121: UnsignedByteType, 32819: UnsignedShort4444Type, - 32820: UnsignedShort5551Type, - 33635: UnsignedShort565Type + 32820: UnsignedShort5551Type }; var WEBGL_SIDES = {