diff --git a/nin/backend/watch.js b/nin/backend/watch.js index 23cb8b0..cf32736 100644 --- a/nin/backend/watch.js +++ b/nin/backend/watch.js @@ -40,6 +40,10 @@ function watch(projectPath, cb) { return; } + if(path.endsWith('~')) { + return; + } + if (logFileChanges) { console.log(chalk.yellow('Change in project detected: ') + chalk.cyan(event) + diff --git a/nin/dasBoot/FullscreenRenderTargetPool.js b/nin/dasBoot/FullscreenRenderTargetPool.js index 238b4bb..66d123f 100644 --- a/nin/dasBoot/FullscreenRenderTargetPool.js +++ b/nin/dasBoot/FullscreenRenderTargetPool.js @@ -21,6 +21,8 @@ class FullscreenRenderTargetPool { this.renderTargets[this.used] = renderTarget; } this.used++; + renderTarget.texture.repeat.set(1, 1); + renderTarget.texture.offset.set(0, 0); return renderTarget; } diff --git a/nin/dasBoot/NodeManager.js b/nin/dasBoot/NodeManager.js index f3f7ddb..9cc10b6 100644 --- a/nin/dasBoot/NodeManager.js +++ b/nin/dasBoot/NodeManager.js @@ -57,7 +57,7 @@ class NodeManager { resize() { for(var key in this.nodes) { - this.nodes[key].resize(); + this.nodes[key].resize(); } } diff --git a/nin/dasBoot/RootNode.js b/nin/dasBoot/RootNode.js index a38c452..3ba0bb1 100644 --- a/nin/dasBoot/RootNode.js +++ b/nin/dasBoot/RootNode.js @@ -4,7 +4,7 @@ class RootNode extends NIN.Node { inputs: {screen: new NIN.TextureInput()} }); - this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); + this.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); this.scene = new THREE.Scene(); this.quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), null); this.scene.add(this.quad); @@ -26,6 +26,8 @@ class RootNode extends NIN.Node { map: A }); } + renderer.setRenderTarget(null); + renderer.clear(); renderer.render(this.scene, this.camera); NIN.FullscreenRenderTargetPool.withdrawFullscreenRenderTargets(); diff --git a/nin/dasBoot/ShaderNode.js b/nin/dasBoot/ShaderNode.js index 1bb750d..bf0d49f 100644 --- a/nin/dasBoot/ShaderNode.js +++ b/nin/dasBoot/ShaderNode.js @@ -30,7 +30,10 @@ class ShaderNode extends NIN.Node { render(renderer) { const renderTarget = NIN.FullscreenRenderTargetPool.getFullscreenRenderTarget(); - renderer.render(this.scene, this.camera, renderTarget, true); + renderer.setRenderTarget(renderTarget); + renderer.clear(); + renderer.render(this.scene, this.camera); + renderer.setRenderTarget(null); this.outputs.render.setValue(renderTarget.texture); } } diff --git a/nin/dasBoot/THREENode.js b/nin/dasBoot/THREENode.js index bb6793c..c637e42 100644 --- a/nin/dasBoot/THREENode.js +++ b/nin/dasBoot/THREENode.js @@ -31,7 +31,10 @@ class THREENode extends NIN.Node { render(renderer) { const renderTarget = NIN.FullscreenRenderTargetPool.getFullscreenRenderTarget(); - renderer.render(this.scene, this.camera, renderTarget, true); + renderer.setRenderTarget(renderTarget); + renderer.clear(); + renderer.render(this.scene, this.camera); + renderer.setRenderTarget(null); this.outputs.render.setValue(renderTarget.texture); } diff --git a/nin/dasBoot/lib/00_three.js b/nin/dasBoot/lib/00_three.js old mode 100755 new mode 100644 index ef6a7b5..1e35bfe --- a/nin/dasBoot/lib/00_three.js +++ b/nin/dasBoot/lib/00_three.js @@ -1,8 +1,8 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.THREE = {}))); -}(this, (function (exports) { 'use strict'; + (global = global || self, factory(global.THREE = {})); +}(this, function (exports) { 'use strict'; // Polyfills @@ -185,7 +185,7 @@ } ); - var REVISION = '92'; + var REVISION = '104'; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; @@ -242,6 +242,8 @@ var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; + var ACESFilmicToneMapping = 5; + var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; @@ -279,6 +281,7 @@ var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; + var RedFormat = 1028; var RGB_S3TC_DXT1_Format = 33776; var RGBA_S3TC_DXT1_Format = 33777; var RGBA_S3TC_DXT3_Format = 33778; @@ -324,6 +327,8 @@ var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; + var TangentSpaceNormalMap = 0; + var ObjectSpaceNormalMap = 1; /** * @author alteredq / http://alteredqualia.com/ @@ -756,21 +761,14 @@ }, - clampScalar: function () { - - var min = new Vector2(); - var max = new Vector2(); - - return function clampScalar( minVal, maxVal ) { - - min.set( minVal, minVal ); - max.set( maxVal, maxVal ); + clampScalar: function ( minVal, maxVal ) { - return this.clamp( min, max ); + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - }; + return this; - }(), + }, clampLength: function ( min, max ) { @@ -831,6 +829,12 @@ }, + cross: function ( v ) { + + return this.x * v.y - this.y * v.x; + + }, + lengthSq: function () { return this.x * this.x + this.y * this.y; @@ -968,5109 +972,5205 @@ } ); /** - * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author jordi_ros / http://plattsoft.com - * @author D1plo1d / http://github.com/D1plo1d - * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ - * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://clara.io + * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io */ - function Matrix4() { + function Quaternion( x, y, z, w ) { - this.elements = [ + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ]; + Object.assign( Quaternion, { - if ( arguments.length > 0 ) { + slerp: function ( qa, qb, qm, t ) { - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + return qm.copy( qa ).slerp( qb, t ); - } + }, - } + slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - Object.assign( Matrix4.prototype, { + // fuzz-free, array-based Quaternion SLERP operation - isMatrix4: true, + var x0 = src0[ srcOffset0 + 0 ], + y0 = src0[ srcOffset0 + 1 ], + z0 = src0[ srcOffset0 + 2 ], + w0 = src0[ srcOffset0 + 3 ], - set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + x1 = src1[ srcOffset1 + 0 ], + y1 = src1[ srcOffset1 + 1 ], + z1 = src1[ srcOffset1 + 2 ], + w1 = src1[ srcOffset1 + 3 ]; - var te = this.elements; + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - 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; + var s = 1 - t, - return this; + cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - }, + dir = ( cos >= 0 ? 1 : - 1 ), + sqrSin = 1 - cos * cos; - identity: function () { + // Skip the Slerp for tiny steps to avoid numeric problems: + if ( sqrSin > Number.EPSILON ) { - this.set( + var sin = Math.sqrt( sqrSin ), + len = Math.atan2( sin, cos * dir ); - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + s = Math.sin( s * len ) / sin; + t = Math.sin( t * len ) / sin; - ); + } - return this; + var tDir = t * dir; - }, + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; - clone: function () { + // Normalize in case we just did a lerp: + if ( s === 1 - t ) { - return new Matrix4().fromArray( this.elements ); + var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - }, + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; - copy: function ( m ) { + } - var te = this.elements; - var me = m.elements; + } - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; + dst[ dstOffset ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; - return this; + } - }, + } ); - copyPosition: function ( m ) { + Object.defineProperties( Quaternion.prototype, { - var te = this.elements, me = m.elements; + x: { - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; + get: function () { - return this; + return this._x; - }, + }, - extractBasis: function ( xAxis, yAxis, zAxis ) { + set: function ( value ) { - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); + this._x = value; + this.onChangeCallback(); - return this; + } }, - makeBasis: function ( xAxis, yAxis, zAxis ) { - - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); + y: { - return this; + get: function () { - }, + return this._y; - extractRotation: function () { + }, - var v1 = new Vector3(); + set: function ( value ) { - return function extractRotation( m ) { + this._y = value; + this.onChangeCallback(); - var te = this.elements; - var me = m.elements; + } - var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); - var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); - var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); + }, - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; + z: { - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; + get: function () { - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; + return this._z; - return this; + }, - }; + set: function ( value ) { - }(), + this._z = value; + this.onChangeCallback(); - makeRotationFromEuler: function ( euler ) { + } - if ( ! ( euler && euler.isEuler ) ) { + }, - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + w: { - } + get: function () { - var te = this.elements; + return this._w; - var x = euler.x, y = euler.y, z = euler.z; - var a = Math.cos( x ), b = Math.sin( x ); - var c = Math.cos( y ), d = Math.sin( y ); - var e = Math.cos( z ), f = Math.sin( z ); + }, - if ( euler.order === 'XYZ' ) { + set: function ( value ) { - var ae = a * e, af = a * f, be = b * e, bf = b * f; + this._w = value; + this.onChangeCallback(); - 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' ) { + Object.assign( Quaternion.prototype, { - var ce = c * e, cf = c * f, de = d * e, df = d * f; + isQuaternion: true, - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; + set: function ( x, y, z, w ) { - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; + this._x = x; + this._y = y; + this._z = z; + this._w = w; - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; + this.onChangeCallback(); - } else if ( euler.order === 'ZXY' ) { + return this; - var ce = c * e, cf = c * f, de = d * e, df = d * f; + }, - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; + clone: function () { - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; + return new this.constructor( this._x, this._y, this._z, this._w ); - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; + }, - } else if ( euler.order === 'ZYX' ) { + copy: function ( quaternion ) { - var ae = a * e, af = a * f, be = b * e, bf = b * f; + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; + this.onChangeCallback(); - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; + return this; - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; + }, - } else if ( euler.order === 'YZX' ) { + setFromEuler: function ( euler, update ) { - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + if ( ! ( euler && euler.isEuler ) ) { - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - 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; + var x = euler._x, y = euler._y, z = euler._z, order = euler.order; - } else if ( euler.order === 'XZY' ) { + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + var cos = Math.cos; + var sin = Math.sin; - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; + var c1 = cos( x / 2 ); + var c2 = cos( y / 2 ); + var c3 = cos( z / 2 ); - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; + var s1 = sin( x / 2 ); + var s2 = sin( y / 2 ); + var s3 = sin( z / 2 ); - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; + if ( order === 'XYZ' ) { - } + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; - // last column - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; + } else if ( order === 'YXZ' ) { - // bottom row - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + 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; - return this; + } else if ( order === 'ZXY' ) { - }, + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; - makeRotationFromQuaternion: function ( q ) { + } else if ( order === 'ZYX' ) { - var te = this.elements; + 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; - var x = q._x, y = q._y, z = q._z, w = q._w; - var x2 = x + x, y2 = y + y, z2 = z + z; - var xx = x * x2, xy = x * y2, xz = x * z2; - var yy = y * y2, yz = y * z2, zz = z * z2; - var wx = w * x2, wy = w * y2, wz = w * z2; + } else if ( order === 'YZX' ) { - te[ 0 ] = 1 - ( yy + zz ); - te[ 4 ] = xy - wz; - te[ 8 ] = xz + wy; + 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; - te[ 1 ] = xy + wz; - te[ 5 ] = 1 - ( xx + zz ); - te[ 9 ] = yz - wx; + } else if ( order === 'XZY' ) { - te[ 2 ] = xz - wy; - te[ 6 ] = yz + wx; - te[ 10 ] = 1 - ( xx + yy ); + 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; - // last column - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; + } - // bottom row - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + if ( update !== false ) this.onChangeCallback(); return this; }, - lookAt: function () { - - var x = new Vector3(); - var y = new Vector3(); - var z = new Vector3(); + setFromAxisAngle: function ( axis, angle ) { - return function lookAt( eye, target, up ) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - var te = this.elements; + // assumes axis is normalized - z.subVectors( eye, target ); + var halfAngle = angle / 2, s = Math.sin( halfAngle ); - if ( z.lengthSq() === 0 ) { + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); - // eye and target are in the same position + this.onChangeCallback(); - z.z = 1; + return this; - } + }, - z.normalize(); - x.crossVectors( up, z ); + setFromRotationMatrix: function ( m ) { - if ( x.lengthSq() === 0 ) { + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - // up and z are parallel + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - if ( Math.abs( up.z ) === 1 ) { + var te = m.elements, - z.x += 0.0001; + 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 ], - } else { + trace = m11 + m22 + m33, + s; - z.z += 0.0001; + if ( trace > 0 ) { - } + s = 0.5 / Math.sqrt( trace + 1.0 ); - z.normalize(); - x.crossVectors( up, z ); + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; - } + } else if ( m11 > m22 && m11 > m33 ) { - x.normalize(); - y.crossVectors( z, x ); + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - 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._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; - return this; + } else if ( m22 > m33 ) { - }; + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - }(), + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; - multiply: function ( m, n ) { + } else { - if ( n !== undefined ) { + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; } - return this.multiplyMatrices( this, m ); + this.onChangeCallback(); - }, + return this; - premultiply: function ( m ) { + }, - return this.multiplyMatrices( m, this ); + setFromUnitVectors: function ( vFrom, vTo ) { - }, + // assumes direction vectors vFrom and vTo are normalized - multiplyMatrices: function ( a, b ) { + var EPS = 0.000001; - var ae = a.elements; - var be = b.elements; - var te = this.elements; + var r = vFrom.dot( vTo ) + 1; - var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + if ( r < EPS ) { - var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + r = 0; - 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; + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - 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; + this._x = - vFrom.y; + this._y = vFrom.x; + this._z = 0; + this._w = r; - 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; + } else { - 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; + this._x = 0; + this._y = - vFrom.z; + this._z = vFrom.y; + this._w = r; - return this; + } - }, + } else { - multiplyScalar: function ( s ) { + // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - var te = this.elements; + 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; - 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; + return this.normalize(); }, - applyToBufferAttribute: function () { + angleTo: function ( q ) { - var v1 = new Vector3(); + return 2 * Math.acos( Math.abs( _Math.clamp( this.dot( q ), - 1, 1 ) ) ); - return function applyToBufferAttribute( attribute ) { + }, - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + rotateTowards: function ( q, step ) { - v1.x = attribute.getX( i ); - v1.y = attribute.getY( i ); - v1.z = attribute.getZ( i ); + var angle = this.angleTo( q ); - v1.applyMatrix4( this ); + if ( angle === 0 ) return this; - attribute.setXYZ( i, v1.x, v1.y, v1.z ); + var t = Math.min( 1, step / angle ); - } + this.slerp( q, t ); - return attribute; + return this; - }; + }, - }(), + inverse: function () { - determinant: function () { + // quaternion is assumed to have unit length - var te = this.elements; + return this.conjugate(); - var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + }, - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + conjugate: function () { - 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 - ) + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; - ); + this.onChangeCallback(); + + return this; }, - transpose: function () { + dot: function ( v ) { - var te = this.elements; - var tmp; + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - 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; + lengthSq: function () { - return this; + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, - setPosition: function ( v ) { - - var te = this.elements; - - te[ 12 ] = v.x; - te[ 13 ] = v.y; - te[ 14 ] = v.z; + length: function () { - return this; + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, - getInverse: function ( m, throwOnDegenerate ) { + normalize: function () { - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - var te = this.elements, - me = m.elements, + var l = this.length(); - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], + if ( l === 0 ) { - 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; + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; - var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + } else { - if ( det === 0 ) { + l = 1 / l; - var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; - if ( throwOnDegenerate === true ) { + } - throw new Error( msg ); + this.onChangeCallback(); - } else { + return this; - console.warn( msg ); + }, - } + multiply: function ( q, p ) { - return this.identity(); + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); } - var detInv = 1 / det; + return this.multiplyQuaternions( this, q ); - 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; + premultiply: function ( q ) { - 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; + return this.multiplyQuaternions( q, this ); - 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; + multiplyQuaternions: function ( a, b ) { - }, + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - scale: function ( v ) { + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - var te = this.elements; - var x = v.x, y = v.y, z = v.z; + 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; - 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; + this.onChangeCallback(); return this; }, - getMaxScaleOnAxis: function () { + slerp: function ( qb, t ) { - var te = this.elements; + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); - var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + var x = this._x, y = this._y, z = this._z, w = this._w; - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - }, + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - makeTranslation: function ( x, y, z ) { + if ( cosHalfTheta < 0 ) { - this.set( + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 + cosHalfTheta = - cosHalfTheta; - ); + } else { - return this; + this.copy( qb ); - }, + } - makeRotationX: function ( theta ) { + if ( cosHalfTheta >= 1.0 ) { - var c = Math.cos( theta ), s = Math.sin( theta ); + this._w = w; + this._x = x; + this._y = y; + this._z = z; - this.set( + return this; - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 + } - ); + var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - return this; + if ( sqrSinHalfTheta <= Number.EPSILON ) { - }, + var 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; - makeRotationY: function ( theta ) { + return this.normalize(); - var c = Math.cos( theta ), s = Math.sin( theta ); + } - this.set( + var sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); + var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 + 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; }, - makeRotationZ: function ( theta ) { + equals: function ( quaternion ) { - var c = Math.cos( theta ), s = Math.sin( theta ); + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - this.set( + }, - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + fromArray: function ( array, offset ) { - ); + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); return this; }, - makeRotationAxis: function ( axis, angle ) { + toArray: function ( array, offset ) { - // Based on http://www.gamedev.net/reference/articles/article1199.asp + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; - var c = Math.cos( angle ); - var s = Math.sin( angle ); - var t = 1 - c; - var x = axis.x, y = axis.y, z = axis.z; - var tx = t * x, ty = t * y; + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; - this.set( + return array; - 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 + }, - ); + onChange: function ( callback ) { - return this; + this.onChangeCallback = callback; + + return this; }, - makeScale: function ( x, y, z ) { + onChangeCallback: function () {} - this.set( + } ); - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 + /** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ - ); + function Vector3( x, y, z ) { - return this; + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; - }, + } - makeShear: function ( x, y, z ) { + Object.assign( Vector3.prototype, { - this.set( + isVector3: true, - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 + set: function ( x, y, z ) { - ); + this.x = x; + this.y = y; + this.z = z; return this; }, - compose: function ( position, quaternion, scale ) { + setScalar: function ( scalar ) { - this.makeRotationFromQuaternion( quaternion ); - this.scale( scale ); - this.setPosition( position ); + this.x = scalar; + this.y = scalar; + this.z = scalar; return this; }, - decompose: function () { + setX: function ( x ) { - var vector = new Vector3(); - var matrix = new Matrix4(); + this.x = x; - return function decompose( position, quaternion, scale ) { + return this; - var te = this.elements; + }, - var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + setY: function ( y ) { - // if determine is negative, we need to invert one scale - var det = this.determinant(); - if ( det < 0 ) sx = - sx; + this.y = y; - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; + return this; - // scale the rotation part - matrix.copy( this ); + }, - var invSX = 1 / sx; - var invSY = 1 / sy; - var invSZ = 1 / sz; + setZ: function ( z ) { - matrix.elements[ 0 ] *= invSX; - matrix.elements[ 1 ] *= invSX; - matrix.elements[ 2 ] *= invSX; + this.z = z; - matrix.elements[ 4 ] *= invSY; - matrix.elements[ 5 ] *= invSY; - matrix.elements[ 6 ] *= invSY; + return this; - matrix.elements[ 8 ] *= invSZ; - matrix.elements[ 9 ] *= invSZ; - matrix.elements[ 10 ] *= invSZ; + }, - quaternion.setFromRotationMatrix( matrix ); + setComponent: function ( index, value ) { - scale.x = sx; - scale.y = sy; - scale.z = sz; + switch ( index ) { - return this; + 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; - makePerspective: function ( left, right, top, bottom, near, far ) { + }, - if ( far === undefined ) { + getComponent: function ( index ) { - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); + 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 ); - var te = this.elements; - var x = 2 * near / ( right - left ); - var y = 2 * near / ( top - bottom ); + } - var a = ( right + left ) / ( right - left ); - var b = ( top + bottom ) / ( top - bottom ); - var c = - ( far + near ) / ( far - near ); - var d = - 2 * far * near / ( far - near ); + }, - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + clone: function () { - return this; + return new this.constructor( this.x, this.y, this.z ); }, - makeOrthographic: function ( left, right, top, bottom, near, far ) { - - var te = this.elements; - var w = 1.0 / ( right - left ); - var h = 1.0 / ( top - bottom ); - var p = 1.0 / ( far - near ); - - var x = ( right + left ) * w; - var y = ( top + bottom ) * h; - var z = ( far + near ) * p; + copy: function ( v ) { - 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.x = v.x; + this.y = v.y; + this.z = v.z; return this; }, - equals: function ( matrix ) { - - var te = this.elements; - var me = matrix.elements; + add: function ( v, w ) { - for ( var i = 0; i < 16; i ++ ) { + if ( w !== undefined ) { - if ( te[ i ] !== me[ i ] ) return false; + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); } - return true; + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; }, - fromArray: function ( array, offset ) { + addScalar: function ( s ) { - if ( offset === undefined ) offset = 0; + this.x += s; + this.y += s; + this.z += s; - for ( var i = 0; i < 16; i ++ ) { + return this; - this.elements[ i ] = array[ i + offset ]; + }, - } + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; return this; }, - toArray: function ( array, offset ) { + addScaledVector: function ( v, s ) { - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; - var te = this.elements; + return this; - 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 ]; + sub: function ( v, w ) { - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; + if ( w !== undefined ) { - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; + 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; - } ); + return this; - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ + }, - function Quaternion( x, y, z, w ) { + subScalar: function ( s ) { - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._w = ( w !== undefined ) ? w : 1; + this.x -= s; + this.y -= s; + this.z -= s; - } + return this; - Object.assign( Quaternion, { + }, - slerp: function ( qa, qb, qm, t ) { + subVectors: function ( a, b ) { - return qm.copy( qa ).slerp( qb, t ); + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; }, - slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { + multiply: function ( v, w ) { - // fuzz-free, array-based Quaternion SLERP operation + if ( w !== undefined ) { - var x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ], + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); - x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; + } - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; - var s = 1 - t, + return this; - cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + }, - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; + multiplyScalar: function ( scalar ) { - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; - var sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); + return this; - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; + }, - } + multiplyVectors: function ( a, b ) { - var tDir = t * dir; + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; + return this; - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { + }, - var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); + applyEuler: function () { - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; + var quaternion = new Quaternion(); - } + return function applyEuler( euler ) { - } + if ( ! ( euler && euler.isEuler ) ) { - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; + console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); - } + } - } ); + return this.applyQuaternion( quaternion.setFromEuler( euler ) ); - Object.defineProperties( Quaternion.prototype, { + }; - x: { + }(), - get: function () { + applyAxisAngle: function () { - return this._x; + var quaternion = new Quaternion(); - }, + return function applyAxisAngle( axis, angle ) { - set: function ( value ) { + return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); - this._x = value; - this.onChangeCallback(); + }; - } + }(), - }, + applyMatrix3: function ( m ) { - y: { + var x = this.x, y = this.y, z = this.z; + var e = m.elements; - get: function () { + 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._y; + return this; - }, + }, - set: function ( value ) { + applyMatrix4: function ( m ) { - this._y = value; - this.onChangeCallback(); + var x = this.x, y = this.y, z = this.z; + var e = m.elements; - } + var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; + + return this; }, - z: { + applyQuaternion: function ( q ) { - get: function () { + var x = this.x, y = this.y, z = this.z; + var qx = q.x, qy = q.y, qz = q.z, qw = q.w; - return this._z; + // calculate quat * vector - }, + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; - set: function ( value ) { + // calculate result * inverse quat - this._z = value; - this.onChangeCallback(); + 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; }, - w: { + project: function ( camera ) { - get: function () { + return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - return this._w; + }, - }, + unproject: function ( camera ) { - set: function ( value ) { + return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - this._w = value; - this.onChangeCallback(); + }, - } + transformDirection: function ( m ) { - } + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction - } ); + var x = this.x, y = this.y, z = this.z; + var e = m.elements; - Object.assign( Quaternion.prototype, { + 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; - set: function ( x, y, z, w ) { + return this.normalize(); - this._x = x; - this._y = y; - this._z = z; - this._w = w; + }, - this.onChangeCallback(); + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; return this; }, - clone: function () { + divideScalar: function ( scalar ) { - return new this.constructor( this._x, this._y, this._z, this._w ); + return this.multiplyScalar( 1 / scalar ); }, - copy: function ( quaternion ) { + min: function ( v ) { - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; + 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.onChangeCallback(); + return this; + + }, + + max: function ( v ) { + + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); return this; }, - setFromEuler: function ( euler, update ) { + clamp: function ( min, max ) { - if ( ! ( euler && euler.isEuler ) ) { + // assumes min < max, componentwise - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + 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; - var x = euler._x, y = euler._y, z = euler._z, order = euler.order; + }, - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m + clampScalar: function ( minVal, maxVal ) { - var cos = Math.cos; - var sin = Math.sin; + 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 ) ); - var c1 = cos( x / 2 ); - var c2 = cos( y / 2 ); - var c3 = cos( z / 2 ); + return this; - var s1 = sin( x / 2 ); - var s2 = sin( y / 2 ); - var s3 = sin( z / 2 ); + }, - if ( order === 'XYZ' ) { + clampLength: function ( min, max ) { - 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; + var length = this.length(); - } else if ( order === 'YXZ' ) { + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; + }, - } else if ( order === 'ZXY' ) { + floor: function () { - 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; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); - } else if ( order === 'ZYX' ) { + return this; - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; + }, - } else if ( order === 'YZX' ) { + ceil: function () { - 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; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); - } else if ( order === 'XZY' ) { + return this; - 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; + }, - } + round: function () { - if ( update !== false ) this.onChangeCallback(); + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); return this; }, - setFromAxisAngle: function ( axis, angle ) { + roundToZero: function () { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + 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 ); - // assumes axis is normalized + return this; - var halfAngle = angle / 2, s = Math.sin( halfAngle ); + }, - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); + negate: function () { - this.onChangeCallback(); + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; return this; }, - setFromRotationMatrix: function ( m ) { + dot: function ( v ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + return this.x * v.x + this.y * v.y + this.z * v.z; - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + }, - var te = m.elements, + // TODO lengthSquared? - 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 ], + lengthSq: function () { - trace = m11 + m22 + m33, - s; + return this.x * this.x + this.y * this.y + this.z * this.z; - if ( trace > 0 ) { + }, - s = 0.5 / Math.sqrt( trace + 1.0 ); + length: function () { - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - } else if ( m11 > m22 && m11 > m33 ) { + }, - s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + manhattanLength: function () { - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - } else if ( m22 > m33 ) { + }, - s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + normalize: function () { - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; + return this.divideScalar( this.length() || 1 ); - } else { + }, - s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + setLength: function ( length ) { - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; + return this.normalize().multiplyScalar( length ); - } + }, - this.onChangeCallback(); + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; return this; }, - setFromUnitVectors: function () { + lerpVectors: function ( v1, v2, alpha ) { - // assumes direction vectors vFrom and vTo are normalized + return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - var v1 = new Vector3(); - var r; + }, - var EPS = 0.000001; + cross: function ( v, w ) { - return function setFromUnitVectors( vFrom, vTo ) { + if ( w !== undefined ) { - if ( v1 === undefined ) v1 = new Vector3(); + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); - r = vFrom.dot( vTo ) + 1; + } - if ( r < EPS ) { + return this.crossVectors( this, v ); - r = 0; + }, - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + crossVectors: function ( a, b ) { - v1.set( - vFrom.y, vFrom.x, 0 ); + var ax = a.x, ay = a.y, az = a.z; + var 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; - v1.set( 0, - vFrom.z, vFrom.y ); + return this; - } + }, - } else { + projectOnVector: function ( vector ) { - v1.crossVectors( vFrom, vTo ); + var scalar = vector.dot( this ) / vector.lengthSq(); - } + return this.copy( vector ).multiplyScalar( scalar ); - this._x = v1.x; - this._y = v1.y; - this._z = v1.z; - this._w = r; + }, - return this.normalize(); + projectOnPlane: function () { + + var v1 = new Vector3(); + + return function projectOnPlane( planeNormal ) { + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); }; }(), - inverse: function () { + reflect: function () { - // quaternion is assumed to have unit length + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length - return this.conjugate(); + var v1 = new Vector3(); - }, + return function reflect( normal ) { - conjugate: function () { + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; + }; - this.onChangeCallback(); + }(), - return this; + angleTo: function ( v ) { - }, + var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); - dot: function ( v ) { + // clamp, to handle numerical problems - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + return Math.acos( _Math.clamp( theta, - 1, 1 ) ); }, - lengthSq: function () { + distanceTo: function ( v ) { - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + return Math.sqrt( this.distanceToSquared( v ) ); }, - length: function () { + distanceToSquared: function ( v ) { - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; }, - normalize: function () { + manhattanDistanceTo: function ( v ) { - var l = this.length(); + return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - if ( l === 0 ) { + }, - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; + setFromSpherical: function ( s ) { - } else { + return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - l = 1 / l; + }, - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; + setFromSphericalCoords: function ( radius, phi, theta ) { - } + var sinPhiRadius = Math.sin( phi ) * radius; - this.onChangeCallback(); + this.x = sinPhiRadius * Math.sin( theta ); + this.y = Math.cos( phi ) * radius; + this.z = sinPhiRadius * Math.cos( theta ); return this; }, - multiply: function ( q, p ) { + setFromCylindrical: function ( c ) { - if ( p !== undefined ) { + return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); + }, - } + setFromCylindricalCoords: function ( radius, theta, y ) { - return this.multiplyQuaternions( this, q ); + this.x = radius * Math.sin( theta ); + this.y = y; + this.z = radius * Math.cos( theta ); + + return this; }, - premultiply: function ( q ) { + setFromMatrixPosition: function ( m ) { - return this.multiplyQuaternions( q, this ); + var e = m.elements; - }, + this.x = e[ 12 ]; + this.y = e[ 13 ]; + this.z = e[ 14 ]; - multiplyQuaternions: function ( a, b ) { + return this; - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + }, - var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + setFromMatrixScale: function ( m ) { - 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; + var sx = this.setFromMatrixColumn( m, 0 ).length(); + var sy = this.setFromMatrixColumn( m, 1 ).length(); + var sz = this.setFromMatrixColumn( m, 2 ).length(); - this.onChangeCallback(); + this.x = sx; + this.y = sy; + this.z = sz; return this; }, - slerp: function ( qb, t ) { + setFromMatrixColumn: function ( m, index ) { - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); + return this.fromArray( m.elements, index * 4 ); - var x = this._x, y = this._y, z = this._z, w = this._w; + }, - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + equals: function ( v ) { - var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - if ( cosHalfTheta < 0 ) { + }, - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; + fromArray: function ( array, offset ) { - cosHalfTheta = - cosHalfTheta; + if ( offset === undefined ) offset = 0; - } else { + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; - this.copy( qb ); + return this; - } + }, - if ( cosHalfTheta >= 1.0 ) { + toArray: function ( array, offset ) { - this._w = w; - this._x = x; - this._y = y; - this._z = z; + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; - return this; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; - } + return array; - var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + }, - if ( Math.abs( sinHalfTheta ) < 0.001 ) { + fromBufferAttribute: function ( attribute, index, offset ) { - this._w = 0.5 * ( w + this._w ); - this._x = 0.5 * ( x + this._x ); - this._y = 0.5 * ( y + this._y ); - this._z = 0.5 * ( z + this._z ); + if ( offset !== undefined ) { - return this; + console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); } - var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this.onChangeCallback(); + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); return this; - }, - - equals: function ( quaternion ) { + } - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + } ); - }, + /** + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + * @author tschw + */ - fromArray: function ( array, offset ) { + function Matrix3() { - if ( offset === undefined ) offset = 0; + this.elements = [ - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 - this.onChangeCallback(); + ]; - return this; + if ( arguments.length > 0 ) { - }, + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + } - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; + Object.assign( Matrix3.prototype, { - return array; + isMatrix3: true, - }, + set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - onChange: function ( callback ) { + var te = this.elements; - this.onChangeCallback = callback; + 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; }, - onChangeCallback: function () {} - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Vector3( x, y, z ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - - } - - Object.assign( Vector3.prototype, { + identity: function () { - isVector3: true, + this.set( - set: function ( x, y, z ) { + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 - this.x = x; - this.y = y; - this.z = z; + ); return this; }, - setScalar: function ( scalar ) { - - this.x = scalar; - this.y = scalar; - this.z = scalar; + clone: function () { - return this; + return new this.constructor().fromArray( this.elements ); }, - setX: function ( x ) { + copy: function ( m ) { - this.x = x; + var te = this.elements; + var me = m.elements; + + te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; + te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; + te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; return this; }, - setY: function ( y ) { - - this.y = y; + setFromMatrix4: function ( m ) { - return this; + var me = m.elements; - }, + this.set( - setZ: function ( z ) { + me[ 0 ], me[ 4 ], me[ 8 ], + me[ 1 ], me[ 5 ], me[ 9 ], + me[ 2 ], me[ 6 ], me[ 10 ] - this.z = z; + ); return this; }, - setComponent: function ( index, value ) { + applyToBufferAttribute: function () { - switch ( index ) { + var v1 = new Vector3(); - 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 function applyToBufferAttribute( attribute ) { - } + for ( var i = 0, l = attribute.count; i < l; i ++ ) { - return this; + v1.x = attribute.getX( i ); + v1.y = attribute.getY( i ); + v1.z = attribute.getZ( i ); - }, + v1.applyMatrix3( this ); - getComponent: function ( index ) { + attribute.setXYZ( i, v1.x, v1.y, v1.z ); - 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 ); + return attribute; - } + }; - }, + }(), - clone: function () { + multiply: function ( m ) { - return new this.constructor( this.x, this.y, this.z ); + return this.multiplyMatrices( this, m ); }, - copy: function ( v ) { - - this.x = v.x; - this.y = v.y; - this.z = v.z; + premultiply: function ( m ) { - return this; + return this.multiplyMatrices( m, this ); }, - add: function ( 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 ); + multiplyMatrices: function ( a, b ) { - } + var ae = a.elements; + var be = b.elements; + var te = this.elements; - this.x += v.x; - this.y += v.y; - this.z += v.z; + var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; + var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; + var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - return this; + var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; + var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; + var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - }, + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; + te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; + te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - addScalar: function ( s ) { + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; + te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; + te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - this.x += s; - this.y += s; - this.z += s; + 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; }, - addVectors: function ( a, b ) { + multiplyScalar: function ( s ) { - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; + var te = this.elements; + + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; }, - addScaledVector: function ( v, s ) { + determinant: function () { - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; + var te = this.elements; - return this; + var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; }, - sub: function ( v, w ) { + getInverse: function ( matrix, throwOnDegenerate ) { - if ( w !== undefined ) { + if ( matrix && matrix.isMatrix4 ) { - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); } - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; + var me = matrix.elements, + te = this.elements, - return this; + n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], + n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], + n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], - }, + t11 = n33 * n22 - n32 * n23, + t12 = n32 * n13 - n33 * n12, + t13 = n23 * n12 - n22 * n13, - subScalar: function ( s ) { + det = n11 * t11 + n21 * t12 + n31 * t13; - this.x -= s; - this.y -= s; - this.z -= s; + if ( det === 0 ) { - return this; + var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; - }, + if ( throwOnDegenerate === true ) { - subVectors: function ( a, b ) { + throw new Error( msg ); - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; + } else { - return this; + console.warn( msg ); - }, + } - multiply: function ( v, w ) { + return this.identity(); - if ( w !== undefined ) { + } - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); + var detInv = 1 / det; - } + te[ 0 ] = t11 * detInv; + te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; + te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; + 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; }, - multiplyScalar: function ( scalar ) { + transpose: function () { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; + var tmp, m = this.elements; + + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; }, - multiplyVectors: function ( a, b ) { - - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; + getNormalMatrix: function ( matrix4 ) { - return this; + return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); }, - applyEuler: function () { + transposeIntoArray: function ( r ) { - var quaternion = new Quaternion(); + var m = this.elements; - return function applyEuler( euler ) { + 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 ]; - if ( ! ( euler && euler.isEuler ) ) { + return this; - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + }, - } + setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { - return this.applyQuaternion( quaternion.setFromEuler( euler ) ); + var c = Math.cos( rotation ); + var s = Math.sin( rotation ); - }; + this.set( + sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, + - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, + 0, 0, 1 + ); - }(), + }, - applyAxisAngle: function () { + scale: function ( sx, sy ) { - var quaternion = new Quaternion(); + var te = this.elements; - return function applyAxisAngle( axis, angle ) { + te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; + te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; - return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + return this; - }; + }, - }(), + rotate: function ( theta ) { - applyMatrix3: function ( m ) { + var c = Math.cos( theta ); + var s = Math.sin( theta ); - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + var te = this.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; + var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; + var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; + + te[ 0 ] = c * a11 + s * a21; + te[ 3 ] = c * a12 + s * a22; + te[ 6 ] = c * a13 + s * a23; + + te[ 1 ] = - s * a11 + c * a21; + te[ 4 ] = - s * a12 + c * a22; + te[ 7 ] = - s * a13 + c * a23; return this; }, - applyMatrix4: function ( m ) { - - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + translate: function ( tx, ty ) { - var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); + var te = this.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; + 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; }, - applyQuaternion: function ( q ) { - - var x = this.x, y = this.y, z = this.z; - var qx = q.x, qy = q.y, qz = q.z, qw = q.w; + equals: function ( matrix ) { - // calculate quat * vector + var te = this.elements; + var me = matrix.elements; - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = - qx * x - qy * y - qz * z; + for ( var i = 0; i < 9; i ++ ) { - // calculate result * inverse quat + if ( te[ i ] !== me[ i ] ) return false; - 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; + return true; }, - project: function () { - - var matrix = new Matrix4(); + fromArray: function ( array, offset ) { - return function project( camera ) { + if ( offset === undefined ) offset = 0; - matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); - return this.applyMatrix4( matrix ); + for ( var i = 0; i < 9; i ++ ) { - }; + this.elements[ i ] = array[ i + offset ]; - }(), + } - unproject: function () { + return this; - var matrix = new Matrix4(); + }, - return function unproject( camera ) { + toArray: function ( array, offset ) { - matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); - return this.applyMatrix4( matrix ); + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; - }; + var te = this.elements; - }(), + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; - transformDirection: function ( m ) { + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + return array; - 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(); + } ); - }, + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - divide: function ( v ) { + var _canvas; - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; + var ImageUtils = { - return this; + getDataURL: function ( image ) { - }, + var canvas; - divideScalar: function ( scalar ) { + if ( typeof HTMLCanvasElement == 'undefined' ) { - return this.multiplyScalar( 1 / scalar ); + return image.src; - }, + } else if ( image instanceof HTMLCanvasElement ) { - min: function ( v ) { + canvas = 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 ); + } else { - return this; + if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - }, + _canvas.width = image.width; + _canvas.height = image.height; - max: function ( v ) { + var context = _canvas.getContext( '2d' ); - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); + if ( image instanceof ImageData ) { - return this; + context.putImageData( image, 0, 0 ); - }, + } else { - clamp: function ( min, max ) { + context.drawImage( image, 0, 0, image.width, image.height ); - // 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 ) ); + canvas = _canvas; - return this; + } - }, + if ( canvas.width > 2048 || canvas.height > 2048 ) { - clampScalar: function () { + return canvas.toDataURL( 'image/jpeg', 0.6 ); - var min = new Vector3(); - var max = new Vector3(); + } else { - return function clampScalar( minVal, maxVal ) { + return canvas.toDataURL( 'image/png' ); - min.set( minVal, minVal, minVal ); - max.set( maxVal, maxVal, maxVal ); + } - return this.clamp( min, max ); + } - }; + }; - }(), - - clampLength: function ( min, max ) { - - var length = this.length(); - - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - - }, - - floor: function () { - - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - - return this; + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - }, + var textureId = 0; - ceil: function () { + function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); + Object.defineProperty( this, 'id', { value: textureId ++ } ); - return this; + this.uuid = _Math.generateUUID(); - }, + this.name = ''; - round: function () { + this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; + this.mipmaps = []; - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); + this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; - return this; + this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; - }, + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; - roundToZero: function () { + this.anisotropy = anisotropy !== undefined ? anisotropy : 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 ); + this.format = format !== undefined ? format : RGBAFormat; + this.type = type !== undefined ? type : UnsignedByteType; - return this; + 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(); - negate: function () { + 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.x = - this.x; - this.y = - this.y; - this.z = - this.z; + // 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 !== undefined ? encoding : LinearEncoding; - return this; + this.version = 0; + this.onUpdate = null; - }, + } - dot: function ( v ) { + Texture.DEFAULT_IMAGE = undefined; + Texture.DEFAULT_MAPPING = UVMapping; - return this.x * v.x + this.y * v.y + this.z * v.z; + Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - }, + constructor: Texture, - // TODO lengthSquared? + isTexture: true, - lengthSq: function () { + updateMatrix: function () { - return this.x * this.x + this.y * this.y + this.z * this.z; + this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); }, - length: function () { + clone: function () { - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + return new this.constructor().copy( this ); }, - manhattanLength: function () { + copy: function ( source ) { - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + this.name = source.name; - }, + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); - normalize: function () { + this.mapping = source.mapping; - return this.divideScalar( this.length() || 1 ); + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; - }, + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; - setLength: function ( length ) { + this.anisotropy = source.anisotropy; - return this.normalize().multiplyScalar( length ); + this.format = source.format; + this.type = source.type; - }, + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + this.center.copy( source.center ); + this.rotation = source.rotation; - lerp: function ( v, alpha ) { + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrix.copy( source.matrix ); - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; return this; }, - lerpVectors: function ( v1, v2, alpha ) { - - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - - }, + toJSON: function ( meta ) { - cross: function ( v, w ) { + var isRootObject = ( meta === undefined || typeof meta === 'string' ); - if ( w !== undefined ) { + if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); + return meta.textures[ this.uuid ]; } - return this.crossVectors( this, v ); + var output = { - }, + metadata: { + version: 4.5, + type: 'Texture', + generator: 'Texture.toJSON' + }, - crossVectors: function ( a, b ) { + uuid: this.uuid, + name: this.name, - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; + mapping: this.mapping, - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; + 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, - projectOnVector: function ( vector ) { + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, - var scalar = vector.dot( this ) / vector.lengthSq(); + flipY: this.flipY, - return this.copy( vector ).multiplyScalar( scalar ); + premultiplyAlpha: this.premultiplyAlpha, + unpackAlignment: this.unpackAlignment - }, + }; - projectOnPlane: function () { + if ( this.image !== undefined ) { - var v1 = new Vector3(); + // TODO: Move to THREE.Image - return function projectOnPlane( planeNormal ) { + var image = this.image; - v1.copy( this ).projectOnVector( planeNormal ); + if ( image.uuid === undefined ) { - return this.sub( v1 ); + image.uuid = _Math.generateUUID(); // UGH - }; + } - }(), + if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { - reflect: function () { + var url; - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length + if ( Array.isArray( image ) ) { - var v1 = new Vector3(); + // process array of images e.g. CubeTexture - return function reflect( normal ) { + url = []; - return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + for ( var i = 0, l = image.length; i < l; i ++ ) { - }; + url.push( ImageUtils.getDataURL( image[ i ] ) ); - }(), + } - angleTo: function ( v ) { + } else { - var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); + // process single image - // clamp, to handle numerical problems + url = ImageUtils.getDataURL( image ); - return Math.acos( _Math.clamp( theta, - 1, 1 ) ); + } - }, + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: url + }; - distanceTo: function ( v ) { + } - return Math.sqrt( this.distanceToSquared( v ) ); + output.image = image.uuid; - }, + } - distanceToSquared: function ( v ) { + if ( ! isRootObject ) { - var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + meta.textures[ this.uuid ] = output; - return dx * dx + dy * dy + dz * dz; + } + + return output; }, - manhattanDistanceTo: function ( v ) { + dispose: function () { - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); + this.dispatchEvent( { type: 'dispose' } ); }, - setFromSpherical: function ( s ) { - - var sinPhiRadius = Math.sin( s.phi ) * s.radius; + transformUv: function ( uv ) { - this.x = sinPhiRadius * Math.sin( s.theta ); - this.y = Math.cos( s.phi ) * s.radius; - this.z = sinPhiRadius * Math.cos( s.theta ); + if ( this.mapping !== UVMapping ) return uv; - return this; + uv.applyMatrix3( this.matrix ); - }, + if ( uv.x < 0 || uv.x > 1 ) { - setFromCylindrical: function ( c ) { + switch ( this.wrapS ) { - this.x = c.radius * Math.sin( c.theta ); - this.y = c.y; - this.z = c.radius * Math.cos( c.theta ); + case RepeatWrapping: - return this; + uv.x = uv.x - Math.floor( uv.x ); + break; - }, + case ClampToEdgeWrapping: - setFromMatrixPosition: function ( m ) { + uv.x = uv.x < 0 ? 0 : 1; + break; - var e = m.elements; + case MirroredRepeatWrapping: - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - return this; + uv.x = Math.ceil( uv.x ) - uv.x; - }, + } else { - setFromMatrixScale: function ( m ) { + uv.x = uv.x - Math.floor( uv.x ); - var sx = this.setFromMatrixColumn( m, 0 ).length(); - var sy = this.setFromMatrixColumn( m, 1 ).length(); - var sz = this.setFromMatrixColumn( m, 2 ).length(); + } + break; - this.x = sx; - this.y = sy; - this.z = sz; + } - return this; + } - }, + if ( uv.y < 0 || uv.y > 1 ) { - setFromMatrixColumn: function ( m, index ) { + switch ( this.wrapT ) { - return this.fromArray( m.elements, index * 4 ); + case RepeatWrapping: - }, + uv.y = uv.y - Math.floor( uv.y ); + break; - equals: function ( v ) { + case ClampToEdgeWrapping: - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + uv.y = uv.y < 0 ? 0 : 1; + break; - }, + case MirroredRepeatWrapping: - fromArray: function ( array, offset ) { + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - if ( offset === undefined ) offset = 0; + uv.y = Math.ceil( uv.y ) - uv.y; - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; + } else { - return this; + uv.y = uv.y - Math.floor( uv.y ); - }, + } + break; - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + } - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; + if ( this.flipY ) { - return array; + uv.y = 1 - uv.y; - }, + } - fromBufferAttribute: function ( attribute, index, offset ) { + return uv; - if ( offset !== undefined ) { + } - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); + } ); - } + Object.defineProperty( Texture.prototype, "needsUpdate", { - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); + set: function ( value ) { - return this; + if ( value === true ) this.version ++; } } ); /** - * @author alteredq / http://alteredqualia.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - * @author tschw */ - function Matrix3() { - - 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.' ); + function Vector4( x, y, z, w ) { - } + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; } - Object.assign( Matrix3.prototype, { - - isMatrix3: true, + Object.assign( Vector4.prototype, { - set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + isVector4: true, - var te = this.elements; + set: function ( x, y, z, w ) { - 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; + this.x = x; + this.y = y; + this.z = z; + this.w = w; return this; }, - identity: function () { - - this.set( - - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 + setScalar: function ( scalar ) { - ); + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; return this; }, - clone: function () { + setX: function ( x ) { - return new this.constructor().fromArray( this.elements ); + this.x = x; - }, + return this; - copy: function ( m ) { + }, - var te = this.elements; - var me = m.elements; + setY: function ( y ) { - 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 ]; + this.y = y; return this; }, - setFromMatrix4: function ( m ) { + setZ: function ( z ) { - var me = m.elements; + this.z = z; - this.set( + return this; - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] + }, - ); + setW: function ( w ) { + + this.w = w; return this; }, - applyToBufferAttribute: function () { + setComponent: function ( index, value ) { - var v1 = new Vector3(); + switch ( index ) { - return function applyToBufferAttribute( attribute ) { + 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 ); - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + } - v1.x = attribute.getX( i ); - v1.y = attribute.getY( i ); - v1.z = attribute.getZ( i ); + return this; - v1.applyMatrix3( this ); + }, - attribute.setXYZ( i, v1.x, v1.y, v1.z ); + getComponent: function ( index ) { - } + switch ( index ) { - return attribute; + 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 ); - }; + } - }(), + }, - multiply: function ( m ) { + clone: function () { - return this.multiplyMatrices( this, m ); + return new this.constructor( this.x, this.y, this.z, this.w ); }, - premultiply: function ( m ) { - - return this.multiplyMatrices( m, this ); + copy: function ( v ) { - }, + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; - multiplyMatrices: function ( a, b ) { + return this; - var ae = a.elements; - var be = b.elements; - var te = this.elements; + }, - var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; + add: function ( v, w ) { - var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; + if ( w !== undefined ) { - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); - 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; + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; return this; }, - multiplyScalar: function ( s ) { - - var te = this.elements; + addScalar: function ( s ) { - 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; + this.x += s; + this.y += s; + this.z += s; + this.w += s; return this; }, - determinant: function () { - - var te = this.elements; + addVectors: function ( a, b ) { - var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + 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 a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + return this; }, - getInverse: function ( matrix, throwOnDegenerate ) { - - if ( matrix && matrix.isMatrix4 ) { + addScaledVector: function ( v, s ) { - console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; - } + return this; - var me = matrix.elements, - te = this.elements, + }, - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], + sub: function ( v, w ) { - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, + if ( w !== undefined ) { - det = n11 * t11 + n21 * t12 + n31 * t13; + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); - if ( det === 0 ) { + } - var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; - if ( throwOnDegenerate === true ) { + return this; - throw new Error( msg ); + }, - } else { + subScalar: function ( s ) { - console.warn( msg ); + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; - } + return this; - return this.identity(); + }, - } + subVectors: function ( a, b ) { - var detInv = 1 / det; + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; + return this; - 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; + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; return this; }, - transpose: function () { + applyMatrix4: function ( m ) { - var tmp, m = this.elements; + var x = this.x, y = this.y, z = this.z, w = this.w; + var e = m.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; + 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; }, - getNormalMatrix: function ( matrix4 ) { + divideScalar: function ( scalar ) { - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); + return this.multiplyScalar( 1 / scalar ); }, - transposeIntoArray: function ( r ) { - - var m = this.elements; - - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; + setAxisAngleFromQuaternion: function ( q ) { - return this; + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - }, + // q is assumed to be normalized - setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { + this.w = 2 * Math.acos( q.w ); - var c = Math.cos( rotation ); - var s = Math.sin( rotation ); + var s = Math.sqrt( 1 - q.w * q.w ); - 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 - ); + if ( s < 0.0001 ) { - }, + this.x = 1; + this.y = 0; + this.z = 0; - scale: function ( sx, sy ) { + } else { - var te = this.elements; + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; + } return this; }, - rotate: function ( theta ) { + setAxisAngleFromRotationMatrix: function ( m ) { - var c = Math.cos( theta ); - var s = Math.sin( theta ); + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - var te = this.elements; + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; + te = m.elements, - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; + 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 ]; - return this; + if ( ( Math.abs( m12 - m21 ) < epsilon ) && + ( Math.abs( m13 - m31 ) < epsilon ) && + ( Math.abs( m23 - m32 ) < epsilon ) ) { - }, + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms - translate: function ( tx, ty ) { + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && + ( Math.abs( m13 + m31 ) < epsilon2 ) && + ( Math.abs( m23 + m32 ) < epsilon2 ) && + ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - var te = this.elements; + // this singularity is identity matrix so angle = 0 - 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 ]; + this.set( 1, 0, 0, 0 ); - return this; + return this; // zero angle, arbitrary axis - }, + } - equals: function ( matrix ) { + // otherwise this singularity is angle = 180 - var te = this.elements; - var me = matrix.elements; + angle = Math.PI; - for ( var i = 0; i < 9; i ++ ) { + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; - if ( te[ i ] !== me[ i ] ) return false; + if ( ( xx > yy ) && ( xx > zz ) ) { - } + // m11 is the largest diagonal term - return true; + if ( xx < epsilon ) { - }, + x = 0; + y = 0.707106781; + z = 0.707106781; - fromArray: function ( array, offset ) { + } else { - if ( offset === undefined ) offset = 0; + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; - for ( var i = 0; i < 9; i ++ ) { + } - this.elements[ i ] = array[ i + offset ]; + } else if ( yy > zz ) { - } + // m22 is the largest diagonal term - return this; + if ( yy < epsilon ) { - }, + x = 0.707106781; + y = 0; + z = 0.707106781; - toArray: function ( array, offset ) { + } else { - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; - var te = this.elements; + } - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; + } else { - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; + // m33 is the largest diagonal term so base result on this - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; + if ( zz < epsilon ) { - return array; + x = 0.707106781; + y = 0.707106781; + z = 0; - } + } else { - } ); + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ + } - var textureId = 0; + } - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { + this.set( x, y, z, angle ); - Object.defineProperty( this, 'id', { value: textureId ++ } ); + return this; // return 180 deg rotation - this.uuid = _Math.generateUUID(); + } - this.name = ''; + // as we have reached here there are no singularities so we can handle normally - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; + if ( Math.abs( s ) < 0.001 ) s = 1; - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; + // 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 - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + return this; - this.format = format !== undefined ? format : RGBAFormat; - this.type = type !== undefined ? type : UnsignedByteType; + }, - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; + min: function ( v ) { - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); + 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 ); - 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) + return this; - // 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 !== undefined ? encoding : LinearEncoding; + }, - this.version = 0; - this.onUpdate = null; + max: function ( v ) { - } + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; + return this; - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + }, - constructor: Texture, + clamp: function ( min, max ) { - isTexture: true, + // assumes min < max, componentwise - updateMatrix: function () { + 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 ) ); - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); + return this; }, - clone: function () { + clampScalar: function () { - return new this.constructor().copy( this ); + var min, max; - }, + return function clampScalar( minVal, maxVal ) { - copy: function ( source ) { + if ( min === undefined ) { - this.name = source.name; + min = new Vector4(); + max = new Vector4(); - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); + } - this.mapping = source.mapping; + min.set( minVal, minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal, maxVal ); - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + return this.clamp( min, max ); - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + }; - this.anisotropy = source.anisotropy; + }(), - this.format = source.format; - this.type = source.type; + clampLength: function ( min, max ) { - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; + var length = this.length(); - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; + }, + + floor: function () { + + 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; }, - toJSON: function ( meta ) { + ceil: function () { - var isRootObject = ( meta === undefined || typeof meta === 'string' ); + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { + return this; - return meta.textures[ this.uuid ]; + }, - } + round: function () { - function getDataURL( image ) { + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); - var canvas; + return this; - if ( image instanceof HTMLCanvasElement ) { + }, - canvas = image; + roundToZero: function () { - } else { + 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 ); - canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = image.width; - canvas.height = image.height; + return this; - var context = canvas.getContext( '2d' ); + }, - if ( image instanceof ImageData ) { + negate: function () { - context.putImageData( image, 0, 0 ); + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; - } else { + return this; - context.drawImage( image, 0, 0, image.width, image.height ); + }, - } + dot: function ( v ) { - } + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - if ( canvas.width > 2048 || canvas.height > 2048 ) { + }, - return canvas.toDataURL( 'image/jpeg', 0.6 ); + lengthSq: function () { - } else { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - return canvas.toDataURL( 'image/png' ); + }, - } + length: function () { - } + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - var output = { + }, - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, + manhattanLength: function () { - uuid: this.uuid, - name: this.name, + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - 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, + normalize: function () { - wrap: [ this.wrapS, this.wrapT ], + return this.divideScalar( this.length() || 1 ); - format: this.format, - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, + }, - flipY: this.flipY + setLength: function ( length ) { - }; + return this.normalize().multiplyScalar( length ); - if ( this.image !== undefined ) { + }, - // TODO: Move to THREE.Image + lerp: function ( v, alpha ) { - var image = this.image; + 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; - if ( image.uuid === undefined ) { + return this; - image.uuid = _Math.generateUUID(); // UGH + }, - } + lerpVectors: function ( v1, v2, alpha ) { - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { + return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: getDataURL( image ) - }; + }, - } + equals: function ( v ) { - output.image = image.uuid; + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - } + }, - if ( ! isRootObject ) { + fromArray: function ( array, offset ) { - meta.textures[ this.uuid ] = output; + if ( offset === undefined ) offset = 0; - } + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; - return output; + return this; }, - dispose: function () { + toArray: function ( array, offset ) { - this.dispatchEvent( { type: 'dispose' } ); + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; + + return array; }, - transformUv: function ( uv ) { + fromBufferAttribute: function ( attribute, index, offset ) { - if ( this.mapping !== UVMapping ) return; + if ( offset !== undefined ) { - uv.applyMatrix3( this.matrix ); + console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); - if ( uv.x < 0 || uv.x > 1 ) { + } - switch ( this.wrapS ) { + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); + this.w = attribute.getW( index ); - case RepeatWrapping: + return this; - uv.x = uv.x - Math.floor( uv.x ); - break; + } - case ClampToEdgeWrapping: + } ); - uv.x = uv.x < 0 ? 0 : 1; - break; + /** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + * @author Marius Kintel / https://github.com/kintel + */ - case MirroredRepeatWrapping: + /* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers + */ + function WebGLRenderTarget( width, height, options ) { - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + this.width = width; + this.height = height; - uv.x = Math.ceil( uv.x ) - uv.x; + this.scissor = new Vector4( 0, 0, width, height ); + this.scissorTest = false; - } else { + this.viewport = new Vector4( 0, 0, width, height ); - uv.x = uv.x - Math.floor( uv.x ); + options = options || {}; - } - break; + this.texture = new Texture( undefined, undefined, 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.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; - if ( uv.y < 0 || uv.y > 1 ) { + } - switch ( this.wrapT ) { + WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { - case RepeatWrapping: + constructor: WebGLRenderTarget, - uv.y = uv.y - Math.floor( uv.y ); - break; + isWebGLRenderTarget: true, - case ClampToEdgeWrapping: + setSize: function ( width, height ) { - uv.y = uv.y < 0 ? 0 : 1; - break; + if ( this.width !== width || this.height !== height ) { - case MirroredRepeatWrapping: + this.width = width; + this.height = height; - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + this.dispose(); - uv.y = Math.ceil( uv.y ) - uv.y; + } - } else { + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - uv.y = uv.y - Math.floor( uv.y ); + }, - } - break; + clone: function () { - } + return new this.constructor().copy( this ); - } + }, - if ( this.flipY ) { + copy: function ( source ) { - uv.y = 1 - uv.y; + this.width = source.width; + this.height = source.height; - } + this.viewport.copy( source.viewport ); - } + this.texture = source.texture.clone(); - } ); + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; - Object.defineProperty( Texture.prototype, "needsUpdate", { + return this; - set: function ( value ) { + }, - if ( value === true ) this.version ++; + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); } } ); /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley + * @author Mugen87 / https://github.com/Mugen87 + * @author Matt DesLauriers / @mattdesl */ - function Vector4( x, y, z, w ) { + function WebGLMultisampleRenderTarget( width, height, options ) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; + WebGLRenderTarget.call( this, width, height, options ); + + this.samples = 4; } - Object.assign( Vector4.prototype, { + WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { - isVector4: true, + constructor: WebGLMultisampleRenderTarget, - set: function ( x, y, z, w ) { + isWebGLMultisampleRenderTarget: true, - this.x = x; - this.y = y; - this.z = z; - this.w = w; + copy: function ( source ) { + + WebGLRenderTarget.prototype.copy.call( this, source ); + + this.samples = source.samples; return this; - }, + } - setScalar: function ( scalar ) { + } ); - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; + /** + * @author alteredq / http://alteredqualia.com + */ - return this; + function WebGLRenderTargetCube( width, height, options ) { - }, + WebGLRenderTarget.call( this, width, height, options ); - setX: function ( x ) { + } - this.x = x; + WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); + WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; - return this; + WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; - }, + /** + * @author alteredq / http://alteredqualia.com/ + */ - setY: function ( y ) { + function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - this.y = y; + Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - return this; + this.image = { data: data, width: width, height: height }; - }, + this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - setZ: function ( z ) { + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - this.z = z; + } - return this; + DataTexture.prototype = Object.create( Texture.prototype ); + DataTexture.prototype.constructor = DataTexture; - }, + DataTexture.prototype.isDataTexture = true; - setW: function ( w ) { + /** + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ - this.w = w; + function Box3( min, max ) { - return this; + this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); + this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); - }, + } - setComponent: function ( index, value ) { + Object.assign( Box3.prototype, { - switch ( index ) { + isBox3: true, - 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 ); + set: function ( min, max ) { - } + this.min.copy( min ); + this.max.copy( max ); return this; }, - getComponent: function ( index ) { - - switch ( index ) { + setFromArray: function ( array ) { - 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 ); + var minX = + Infinity; + var minY = + Infinity; + var minZ = + Infinity; - } + var maxX = - Infinity; + var maxY = - Infinity; + var maxZ = - Infinity; - }, + for ( var i = 0, l = array.length; i < l; i += 3 ) { - clone: function () { + var x = array[ i ]; + var y = array[ i + 1 ]; + var z = array[ i + 2 ]; - return new this.constructor( this.x, this.y, this.z, this.w ); + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - }, + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; - copy: function ( v ) { + } - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); return this; }, - add: function ( v, w ) { + setFromBufferAttribute: function ( attribute ) { - if ( w !== undefined ) { + var minX = + Infinity; + var minY = + Infinity; + var minZ = + Infinity; - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + var maxX = - Infinity; + var maxY = - Infinity; + var maxZ = - Infinity; - } + for ( var i = 0, l = attribute.count; i < l; i ++ ) { - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; + var x = attribute.getX( i ); + var y = attribute.getY( i ); + var z = attribute.getZ( i ); - return this; + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( z < minZ ) minZ = z; - }, + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + if ( z > maxZ ) maxZ = z; - addScalar: function ( s ) { + } - this.x += s; - this.y += s; - this.z += s; - this.w += s; + this.min.set( minX, minY, minZ ); + this.max.set( maxX, maxY, maxZ ); return this; }, - addVectors: function ( 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; + setFromPoints: function ( points ) { - return this; + this.makeEmpty(); - }, + for ( var i = 0, il = points.length; i < il; i ++ ) { - addScaledVector: function ( v, s ) { + this.expandByPoint( points[ i ] ); - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; + } return this; }, - sub: function ( v, w ) { + setFromCenterAndSize: function () { - if ( w !== undefined ) { + var v1 = new Vector3(); - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + return function setFromCenterAndSize( center, size ) { - } + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); - return this; + return this; - }, + }; - subScalar: function ( s ) { + }(), - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; + setFromObject: function ( object ) { - return this; + this.makeEmpty(); - }, + return this.expandByObject( object ); - subVectors: function ( 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; + clone: function () { - return this; + return new this.constructor().copy( this ); }, - multiplyScalar: function ( scalar ) { + copy: function ( box ) { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; + this.min.copy( box.min ); + this.max.copy( box.max ); return this; }, - applyMatrix4: function ( m ) { - - var x = this.x, y = this.y, z = this.z, w = this.w; - var e = m.elements; + makeEmpty: function () { - 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.min.x = this.min.y = this.min.z = + Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; return this; }, - divideScalar: function ( scalar ) { + isEmpty: function () { - return this.multiplyScalar( 1 / scalar ); + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); }, - setAxisAngleFromQuaternion: function ( q ) { + getCenter: function ( target ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + if ( target === undefined ) { - // q is assumed to be normalized + console.warn( 'THREE.Box3: .getCenter() target is now required' ); + target = new Vector3(); - this.w = 2 * Math.acos( q.w ); + } - var s = Math.sqrt( 1 - q.w * q.w ); + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - if ( s < 0.0001 ) { + }, - this.x = 1; - this.y = 0; - this.z = 0; + getSize: function ( target ) { - } else { + if ( target === undefined ) { - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; + console.warn( 'THREE.Box3: .getSize() target is now required' ); + target = new Vector3(); } - return this; + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); }, - setAxisAngleFromRotationMatrix: function ( m ) { - - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - - var angle, x, y, z, // variables for result - epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + expandByPoint: function ( point ) { - te = m.elements, + this.min.min( point ); + this.max.max( point ); - 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 ]; + return this; - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { + }, - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms + expandByVector: function ( vector ) { - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + this.min.sub( vector ); + this.max.add( vector ); - // this singularity is identity matrix so angle = 0 + return this; - this.set( 1, 0, 0, 0 ); + }, - return this; // zero angle, arbitrary axis + expandByScalar: function ( scalar ) { - } + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); - // otherwise this singularity is angle = 180 + return this; - angle = Math.PI; + }, - var xx = ( m11 + 1 ) / 2; - var yy = ( m22 + 1 ) / 2; - var zz = ( m33 + 1 ) / 2; - var xy = ( m12 + m21 ) / 4; - var xz = ( m13 + m31 ) / 4; - var yz = ( m23 + m32 ) / 4; + expandByObject: function () { - if ( ( xx > yy ) && ( xx > zz ) ) { + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms - // m11 is the largest diagonal term + var scope, i, l; - if ( xx < epsilon ) { + var v1 = new Vector3(); - x = 0; - y = 0.707106781; - z = 0.707106781; + function traverse( node ) { - } else { + var geometry = node.geometry; - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; + if ( geometry !== undefined ) { - } + if ( geometry.isGeometry ) { - } else if ( yy > zz ) { + var vertices = geometry.vertices; - // m22 is the largest diagonal term + for ( i = 0, l = vertices.length; i < l; i ++ ) { - if ( yy < epsilon ) { + v1.copy( vertices[ i ] ); + v1.applyMatrix4( node.matrixWorld ); - x = 0.707106781; - y = 0; - z = 0.707106781; + scope.expandByPoint( v1 ); - } else { + } - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; + } else if ( geometry.isBufferGeometry ) { - } + var attribute = geometry.attributes.position; - } else { + if ( attribute !== undefined ) { - // m33 is the largest diagonal term so base result on this + for ( i = 0, l = attribute.count; i < l; i ++ ) { - if ( zz < epsilon ) { + v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); - x = 0.707106781; - y = 0.707106781; - z = 0; + scope.expandByPoint( v1 ); - } else { + } - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; + } } } - this.set( x, y, z, angle ); + } - return this; // return 180 deg rotation + return function expandByObject( object ) { - } + scope = this; - // as we have reached here there are no singularities so we can handle normally + object.updateMatrixWorld( true ); - var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + object.traverse( traverse ); - if ( Math.abs( s ) < 0.001 ) s = 1; + return this; - // 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 + }; - 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 this; + containsPoint: function ( point ) { - }, + 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; - min: function ( v ) { + }, - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); + containsBox: function ( box ) { - return this; + 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; }, - max: function ( v ) { - - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); - - return this; + getParameter: function ( point, target ) { - }, + // This can potentially have a divide by zero if the box + // has a size dimension of 0. - clamp: function ( min, max ) { + if ( target === undefined ) { - // assumes min < max, componentwise + console.warn( 'THREE.Box3: .getParameter() target is now required' ); + target = new Vector3(); - 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; + 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 ) + ); }, - clampScalar: function () { + intersectsBox: function ( box ) { - var min, max; + // 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 function clampScalar( minVal, maxVal ) { + }, - if ( min === undefined ) { + intersectsSphere: ( function () { - min = new Vector4(); - max = new Vector4(); + var closestPoint = new Vector3(); - } + return function intersectsSphere( sphere ) { - min.set( minVal, minVal, minVal, minVal ); - max.set( maxVal, maxVal, maxVal, maxVal ); + // Find the point on the AABB closest to the sphere center. + this.clampPoint( sphere.center, closestPoint ); - return this.clamp( min, max ); + // If that point is inside the sphere, the AABB and sphere intersect. + return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); }; - }(), + } )(), - clampLength: function ( min, max ) { + intersectsPlane: function ( plane ) { - var length = this.length(); + // 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. - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + var min, max; - }, + if ( plane.normal.x > 0 ) { - floor: function () { + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; - 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; + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; - }, + } - ceil: function () { + if ( plane.normal.y > 0 ) { - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; - return this; + } else { - }, + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; - round: function () { + } - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); + if ( plane.normal.z > 0 ) { - return this; + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; - }, + } else { - roundToZero: function () { + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; - 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; + return ( min <= - plane.constant && max >= - plane.constant ); }, - negate: function () { + intersectsTriangle: ( function () { - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; + // triangle centered vertices + var v0 = new Vector3(); + var v1 = new Vector3(); + var v2 = new Vector3(); - return this; + // triangle edge vectors + var f0 = new Vector3(); + var f1 = new Vector3(); + var f2 = new Vector3(); - }, + var testAxis = new Vector3(); - dot: function ( v ) { + var center = new Vector3(); + var extents = new Vector3(); - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + var triangleNormal = new Vector3(); - }, + function satForAxes( axes ) { - lengthSq: function () { + var i, j; - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { - }, + testAxis.fromArray( axes, i ); + // project the aabb onto the seperating axis + var 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 + var p0 = v0.dot( testAxis ); + var p1 = v1.dot( testAxis ); + var 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 ) { - length: function () { + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is seperating and we can exit + return false; - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + } - }, + } - manhattanLength: function () { + return true; - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + } - }, + return function intersectsTriangle( triangle ) { - normalize: function () { + if ( this.isEmpty() ) { - return this.divideScalar( this.length() || 1 ); + return false; - }, + } - setLength: function ( length ) { + // compute box center and extents + this.getCenter( center ); + extents.subVectors( this.max, center ); - return this.normalize().multiplyScalar( length ); + // translate triangle to aabb origin + v0.subVectors( triangle.a, center ); + v1.subVectors( triangle.b, center ); + v2.subVectors( triangle.c, center ); - }, + // compute edge vectors for triangle + f0.subVectors( v1, v0 ); + f1.subVectors( v2, v1 ); + f2.subVectors( v0, v2 ); - lerp: function ( v, alpha ) { + // 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) + var 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 ) ) { - 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 false; - return this; + } - }, + // test 3 face normals from the aabb + axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; + if ( ! satForAxes( axes ) ) { - lerpVectors: function ( v1, v2, alpha ) { + return false; - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + } - }, + // 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 satForAxes( axes ); - equals: function ( v ) { + }; - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + } )(), - }, + clampPoint: function ( point, target ) { - fromArray: function ( array, offset ) { + if ( target === undefined ) { - if ( offset === undefined ) 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 ]; + } - return this; + return target.copy( point ).clamp( this.min, this.max ); }, - toArray: function ( array, offset ) { - - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; - - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; - - return array; - - }, + distanceToPoint: function () { - fromBufferAttribute: function ( attribute, index, offset ) { + var v1 = new Vector3(); - if ( offset !== undefined ) { + return function distanceToPoint( point ) { - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); - } + }; - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); + }(), - return this; + getBoundingSphere: function () { - } + var v1 = new Vector3(); - } ); + return function getBoundingSphere( target ) { - /** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - * @author Marius Kintel / https://github.com/kintel - */ + if ( target === undefined ) { - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { + console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); + //target = new Sphere(); // removed to avoid cyclic dependency - this.width = width; - this.height = height; + } - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; + this.getCenter( target.center ); - this.viewport = new Vector4( 0, 0, width, height ); + target.radius = this.getSize( v1 ).length() * 0.5; - options = options || {}; + return target; - if ( options.minFilter === undefined ) options.minFilter = LinearFilter; + }; - this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + }(), - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; + intersect: function ( box ) { - } + this.min.max( box.min ); + this.max.min( box.max ); - WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + // 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(); - constructor: WebGLRenderTarget, + return this; - isWebGLRenderTarget: true, + }, - setSize: function ( width, height ) { + union: function ( box ) { - if ( this.width !== width || this.height !== height ) { + this.min.min( box.min ); + this.max.max( box.max ); - this.width = width; - this.height = height; + return this; - this.dispose(); + }, - } + applyMatrix4: function () { - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); + var points = [ + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3() + ]; - }, + return function applyMatrix4( matrix ) { - clone: function () { + // transform of empty box is an empty box. + if ( this.isEmpty() ) return this; - return new this.constructor().copy( 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 - }, + this.setFromPoints( points ); - copy: function ( source ) { + return this; - this.width = source.width; - this.height = source.height; + }; - this.viewport.copy( source.viewport ); + }(), - this.texture = source.texture.clone(); + translate: function ( offset ) { - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; + this.min.add( offset ); + this.max.add( offset ); return this; }, - dispose: function () { + equals: function ( box ) { - this.dispatchEvent( { type: 'dispose' } ); + return box.min.equals( this.min ) && box.max.equals( this.max ); } } ); /** - * @author alteredq / http://alteredqualia.com + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ */ - function WebGLRenderTargetCube( width, height, options ) { - - WebGLRenderTarget.call( this, width, height, options ); + function Sphere( center, radius ) { - this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 - this.activeMipMapLevel = 0; + this.center = ( center !== undefined ) ? center : new Vector3(); + this.radius = ( radius !== undefined ) ? radius : 0; } - WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; + Object.assign( Sphere.prototype, { - WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; + set: function ( center, radius ) { - /** - * @author alteredq / http://alteredqualia.com/ - */ + this.center.copy( center ); + this.radius = radius; - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + return this; - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + }, - this.image = { data: data, width: width, height: height }; + setFromPoints: function () { - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + var box = new Box3(); - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; + return function setFromPoints( points, optionalCenter ) { - } + var center = this.center; - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; + if ( optionalCenter !== undefined ) { - DataTexture.prototype.isDataTexture = true; + center.copy( optionalCenter ); - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ + } else { - function Box3( min, max ) { + box.setFromPoints( points ).getCenter( center ); - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); + } - } + var maxRadiusSq = 0; - Object.assign( Box3.prototype, { + for ( var i = 0, il = points.length; i < il; i ++ ) { - isBox3: true, + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - set: function ( min, max ) { + } - this.min.copy( min ); - this.max.copy( max ); + this.radius = Math.sqrt( maxRadiusSq ); - return this; + return this; - }, + }; - setFromArray: function ( array ) { + }(), - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; + clone: function () { - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; + return new this.constructor().copy( this ); - for ( var i = 0, l = array.length; i < l; i += 3 ) { + }, - var x = array[ i ]; - var y = array[ i + 1 ]; - var z = array[ i + 2 ]; + copy: function ( sphere ) { - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + this.center.copy( sphere.center ); + this.radius = sphere.radius; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + return this; - } + }, - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + empty: function () { - return this; + return ( this.radius <= 0 ); }, - setFromBufferAttribute: function ( attribute ) { - - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; + containsPoint: function ( point ) { - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + }, - var x = attribute.getX( i ); - var y = attribute.getY( i ); - var z = attribute.getZ( i ); + distanceToPoint: function ( point ) { - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + return ( point.distanceTo( this.center ) - this.radius ); - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + }, - } + intersectsSphere: function ( sphere ) { - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + var radiusSum = this.radius + sphere.radius; - return this; + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); }, - setFromPoints: function ( points ) { - - this.makeEmpty(); + intersectsBox: function ( box ) { - for ( var i = 0, il = points.length; i < il; i ++ ) { + return box.intersectsSphere( this ); - this.expandByPoint( points[ i ] ); + }, - } + intersectsPlane: function ( plane ) { - return this; + return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; }, - setFromCenterAndSize: function () { + clampPoint: function ( point, target ) { - var v1 = new Vector3(); + var deltaLengthSq = this.center.distanceToSquared( point ); - return function setFromCenterAndSize( center, size ) { + if ( target === undefined ) { - var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); + target = new Vector3(); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); + } - return this; - - }; + target.copy( point ); - }(), + if ( deltaLengthSq > ( this.radius * this.radius ) ) { - setFromObject: function ( object ) { + target.sub( this.center ).normalize(); + target.multiplyScalar( this.radius ).add( this.center ); - this.makeEmpty(); + } - return this.expandByObject( object ); + return target; }, - clone: function () { + getBoundingBox: function ( target ) { - return new this.constructor().copy( this ); + if ( target === undefined ) { - }, + console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); + target = new Box3(); - copy: function ( box ) { + } - this.min.copy( box.min ); - this.max.copy( box.max ); + target.set( this.center, this.center ); + target.expandByScalar( this.radius ); - return this; + return target; }, - makeEmpty: function () { + applyMatrix4: function ( matrix ) { - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; }, - isEmpty: function () { + translate: function ( offset ) { - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + this.center.add( offset ); - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + return this; }, - getCenter: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); + equals: function ( sphere ) { - } + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + } - }, + } ); - getSize: function ( target ) { + /** + * @author bhouston / http://clara.io + */ - if ( target === undefined ) { + function Plane( normal, constant ) { - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); + // normal is assumed to be normalized - } + this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); + this.constant = ( constant !== undefined ) ? constant : 0; - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); + } - }, + Object.assign( Plane.prototype, { - expandByPoint: function ( point ) { + set: function ( normal, constant ) { - this.min.min( point ); - this.max.max( point ); + this.normal.copy( normal ); + this.constant = constant; return this; }, - expandByVector: function ( vector ) { + setComponents: function ( x, y, z, w ) { - this.min.sub( vector ); - this.max.add( vector ); + this.normal.set( x, y, z ); + this.constant = w; return this; }, - expandByScalar: function ( scalar ) { + setFromNormalAndCoplanarPoint: function ( normal, point ) { - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); return this; }, - expandByObject: function () { + setFromCoplanarPoints: function () { - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms + var v1 = new Vector3(); + var v2 = new Vector3(); - var scope, i, l; + return function setFromCoplanarPoints( a, b, c ) { - var v1 = new Vector3(); + var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); - function traverse( node ) { + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - var geometry = node.geometry; + this.setFromNormalAndCoplanarPoint( normal, a ); - if ( geometry !== undefined ) { + return this; - if ( geometry.isGeometry ) { + }; - var vertices = geometry.vertices; + }(), - for ( i = 0, l = vertices.length; i < l; i ++ ) { + clone: function () { - v1.copy( vertices[ i ] ); - v1.applyMatrix4( node.matrixWorld ); + return new this.constructor().copy( this ); - scope.expandByPoint( v1 ); + }, - } + copy: function ( plane ) { - } else if ( geometry.isBufferGeometry ) { + this.normal.copy( plane.normal ); + this.constant = plane.constant; - var attribute = geometry.attributes.position; + return this; - if ( attribute !== undefined ) { + }, - for ( i = 0, l = attribute.count; i < l; i ++ ) { + normalize: function () { - v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); + // Note: will lead to a divide by zero if the plane is invalid. - scope.expandByPoint( v1 ); + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; - } + return this; - } + }, - } + negate: function () { - } + this.constant *= - 1; + this.normal.negate(); - } + return this; - return function expandByObject( object ) { + }, - scope = this; + distanceToPoint: function ( point ) { - object.updateMatrixWorld( true ); + return this.normal.dot( point ) + this.constant; - object.traverse( traverse ); + }, - return this; + distanceToSphere: function ( sphere ) { - }; + return this.distanceToPoint( sphere.center ) - sphere.radius; - }(), + }, - containsPoint: function ( point ) { + projectPoint: function ( point, target ) { - 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 ( target === undefined ) { - }, + console.warn( 'THREE.Plane: .projectPoint() target is now required' ); + target = new Vector3(); - containsBox: function ( 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 target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); }, - getParameter: function ( point, target ) { + intersectLine: function () { - // This can potentially have a divide by zero if the box - // has a size dimension of 0. + var v1 = new Vector3(); - if ( target === undefined ) { + return function intersectLine( line, target ) { - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); + if ( target === undefined ) { - } + console.warn( 'THREE.Plane: .intersectLine() 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 ) - ); + } - }, + var direction = line.delta( v1 ); - intersectsBox: function ( box ) { + var denominator = this.normal.dot( direction ); - // 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; + if ( denominator === 0 ) { - }, + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { - intersectsSphere: ( function () { + return target.copy( line.start ); - var closestPoint = new Vector3(); + } - return function intersectsSphere( sphere ) { + // Unsure if this is the correct method to handle this case. + return undefined; - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, closestPoint ); + } - // If that point is inside the sphere, the AABB and sphere intersect. - return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - }; + if ( t < 0 || t > 1 ) { - } )(), + return undefined; - intersectsPlane: function ( plane ) { + } - // 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. + return target.copy( direction ).multiplyScalar( t ).add( line.start ); - var min, max; + }; - if ( plane.normal.x > 0 ) { + }(), - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; + intersectsLine: function ( line ) { - } else { + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); - } + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); - if ( plane.normal.y > 0 ) { + }, - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; + intersectsBox: function ( box ) { - } else { + return box.intersectsPlane( this ); - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; + }, - } + intersectsSphere: function ( sphere ) { - if ( plane.normal.z > 0 ) { + return sphere.intersectsPlane( this ); - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; + }, - } else { + coplanarPoint: function ( target ) { - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; + if ( target === undefined ) { + + console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); + target = new Vector3(); } - return ( min <= plane.constant && max >= plane.constant ); + return target.copy( this.normal ).multiplyScalar( - this.constant ); }, - intersectsTriangle: ( function () { + applyMatrix4: function () { - // triangle centered vertices - var v0 = new Vector3(); var v1 = new Vector3(); - var v2 = new Vector3(); + var m1 = new Matrix3(); - // triangle edge vectors - var f0 = new Vector3(); - var f1 = new Vector3(); - var f2 = new Vector3(); + return function applyMatrix4( matrix, optionalNormalMatrix ) { - var testAxis = new Vector3(); + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); - var center = new Vector3(); - var extents = new Vector3(); + var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); - var triangleNormal = new Vector3(); + var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); - function satForAxes( axes ) { + this.constant = - referencePoint.dot( normal ); - var i, j; + return this; - for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { + }; - testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - var 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 - var p0 = v0.dot( testAxis ); - var p1 = v1.dot( testAxis ); - var 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 ) { + }(), - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; + translate: function ( offset ) { - } + this.constant -= offset.dot( this.normal ); - } + return this; - return true; + }, - } + equals: function ( plane ) { - return function intersectsTriangle( triangle ) { + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - if ( this.isEmpty() ) { + } - return false; + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / http://clara.io + */ - // compute box center and extents - this.getCenter( center ); - extents.subVectors( this.max, center ); + function Frustum( p0, p1, p2, p3, p4, p5 ) { - // translate triangle to aabb origin - v0.subVectors( triangle.a, center ); - v1.subVectors( triangle.b, center ); - v2.subVectors( triangle.c, center ); + this.planes = [ - // compute edge vectors for triangle - f0.subVectors( v1, v0 ); - f1.subVectors( v2, v1 ); - f2.subVectors( v0, v2 ); + ( p0 !== undefined ) ? p0 : new Plane(), + ( p1 !== undefined ) ? p1 : new Plane(), + ( p2 !== undefined ) ? p2 : new Plane(), + ( p3 !== undefined ) ? p3 : new Plane(), + ( p4 !== undefined ) ? p4 : new Plane(), + ( p5 !== undefined ) ? p5 : new Plane() - // 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) - var 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 ) ) { + ]; - return false; + } - } + Object.assign( Frustum.prototype, { - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes ) ) { + set: function ( p0, p1, p2, p3, p4, p5 ) { - return false; + var 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 ); - // 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 satForAxes( axes ); + return this; - }; + }, - } )(), + clone: function () { - clampPoint: function ( point, target ) { + return new this.constructor().copy( this ); - if ( target === undefined ) { + }, - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); + copy: function ( frustum ) { - } + var planes = this.planes; - return target.copy( point ).clamp( this.min, this.max ); + for ( var i = 0; i < 6; i ++ ) { - }, + planes[ i ].copy( frustum.planes[ i ] ); - distanceToPoint: function () { + } - var v1 = new Vector3(); + return this; - return function distanceToPoint( point ) { + }, - var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); + setFromMatrix: function ( m ) { - }; + var planes = this.planes; + var me = m.elements; + var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + var 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(); - getBoundingSphere: function () { + return this; - var v1 = new Vector3(); + }, - return function getBoundingSphere( target ) { + intersectsObject: function () { - if ( target === undefined ) { + var sphere = new Sphere(); - console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' ); - target = new Sphere(); + return function intersectsObject( object ) { - } + var geometry = object.geometry; - this.getCenter( target.center ); + if ( geometry.boundingSphere === null ) + geometry.computeBoundingSphere(); - target.radius = this.getSize( v1 ).length() * 0.5; + sphere.copy( geometry.boundingSphere ) + .applyMatrix4( object.matrixWorld ); - return target; + return this.intersectsSphere( sphere ); }; }(), - intersect: function ( box ) { + intersectsSprite: function () { - this.min.max( box.min ); - this.max.min( box.max ); + var sphere = new Sphere(); - // 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(); + return function intersectsSprite( sprite ) { - return this; + sphere.center.set( 0, 0, 0 ); + sphere.radius = 0.7071067811865476; + sphere.applyMatrix4( sprite.matrixWorld ); - }, + return this.intersectsSphere( sphere ); - union: function ( box ) { + }; - this.min.min( box.min ); - this.max.max( box.max ); + }(), - return this; + intersectsSphere: function ( sphere ) { - }, + var planes = this.planes; + var center = sphere.center; + var negRadius = - sphere.radius; - applyMatrix4: function () { + for ( var i = 0; i < 6; i ++ ) { - var points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; + var distance = planes[ i ].distanceToPoint( center ); - return function applyMatrix4( matrix ) { + if ( distance < negRadius ) { - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; + return false; - // 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; + return true; - }; + }, - }(), + intersectsBox: function () { - translate: function ( offset ) { + var p = new Vector3(); - this.min.add( offset ); - this.max.add( offset ); + return function intersectsBox( box ) { - return this; + var planes = this.planes; - }, + for ( var i = 0; i < 6; i ++ ) { - equals: function ( box ) { + var plane = planes[ i ]; - return box.min.equals( this.min ) && box.max.equals( this.max ); + // corner at max distance - } + p.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p.z = plane.normal.z > 0 ? box.max.z : box.min.z; - } ); + if ( plane.distanceToPoint( p ) < 0 ) { - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ + return false; - function Sphere( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : 0; + } - } + } - Object.assign( Sphere.prototype, { + return true; - set: function ( center, radius ) { + }; - this.center.copy( center ); - this.radius = radius; + }(), - return this; + containsPoint: function ( point ) { - }, + var planes = this.planes; - setFromPoints: function () { + for ( var i = 0; i < 6; i ++ ) { - var box = new Box3(); + if ( planes[ i ].distanceToPoint( point ) < 0 ) { - return function setFromPoints( points, optionalCenter ) { + return false; - var center = this.center; + } - if ( optionalCenter !== undefined ) { + } - center.copy( optionalCenter ); + return true; - } else { + } - box.setFromPoints( points ).getCenter( center ); + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + */ - var maxRadiusSq = 0; + function Matrix4() { - for ( var i = 0, il = points.length; i < il; i ++ ) { + this.elements = [ - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - } + ]; - this.radius = Math.sqrt( maxRadiusSq ); + if ( arguments.length > 0 ) { - return this; + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - }; + } - }(), + } - clone: function () { + Object.assign( Matrix4.prototype, { - return new this.constructor().copy( this ); + isMatrix4: true, - }, + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - copy: function ( sphere ) { + var te = this.elements; - this.center.copy( sphere.center ); - this.radius = sphere.radius; + 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; }, - empty: function () { - - return ( this.radius <= 0 ); - - }, - - containsPoint: function ( point ) { + identity: function () { - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + this.set( - }, + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - distanceToPoint: function ( point ) { + ); - return ( point.distanceTo( this.center ) - this.radius ); + return this; }, - intersectsSphere: function ( sphere ) { - - var radiusSum = this.radius + sphere.radius; + clone: function () { - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + return new Matrix4().fromArray( this.elements ); }, - intersectsBox: function ( box ) { - - return box.intersectsSphere( this ); + copy: function ( m ) { - }, + var te = this.elements; + var me = m.elements; - intersectsPlane: function ( plane ) { + 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 Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; + return this; }, - clampPoint: function ( point, target ) { - - var deltaLengthSq = this.center.distanceToSquared( point ); - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); - - } - - target.copy( point ); - - if ( deltaLengthSq > ( this.radius * this.radius ) ) { + copyPosition: function ( m ) { - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); + var te = this.elements, me = m.elements; - } + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; - return target; + return this; }, - getBoundingBox: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); - - } + extractBasis: function ( xAxis, yAxis, zAxis ) { - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); + xAxis.setFromMatrixColumn( this, 0 ); + yAxis.setFromMatrixColumn( this, 1 ); + zAxis.setFromMatrixColumn( this, 2 ); - return target; + return this; }, - applyMatrix4: function ( matrix ) { + makeBasis: function ( xAxis, yAxis, zAxis ) { - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); + 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; }, - translate: function ( offset ) { + extractRotation: function () { - this.center.add( offset ); + var v1 = new Vector3(); - return this; + return function extractRotation( m ) { - }, + // this method does not support reflection matrices - equals: function ( sphere ) { + var te = this.elements; + var me = m.elements; - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); + var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); + var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); - } + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + te[ 3 ] = 0; - } ); + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + te[ 7 ] = 0; - /** - * @author bhouston / http://clara.io - */ + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + te[ 11 ] = 0; - function Plane( normal, constant ) { + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - // normal is assumed to be normalized + return this; - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; + }; - } + }(), - Object.assign( Plane.prototype, { + makeRotationFromEuler: function ( euler ) { - set: function ( normal, constant ) { + if ( ! ( euler && euler.isEuler ) ) { - this.normal.copy( normal ); - this.constant = constant; + console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); - return this; + } - }, + var te = this.elements; - setComponents: function ( x, y, z, w ) { + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); - this.normal.set( x, y, z ); - this.constant = w; + if ( euler.order === 'XYZ' ) { - return this; + var ae = a * e, af = a * f, be = b * e, bf = b * f; - }, + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; - setFromNormalAndCoplanarPoint: function ( normal, point ) { + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; - return this; + } else if ( euler.order === 'YXZ' ) { - }, + var ce = c * e, cf = c * f, de = d * e, df = d * f; - setFromCoplanarPoints: function () { + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; - var v1 = new Vector3(); - var v2 = new Vector3(); + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; - return function setFromCoplanarPoints( a, b, c ) { + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; - var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); + } else if ( euler.order === 'ZXY' ) { - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + var ce = c * e, cf = c * f, de = d * e, df = d * f; - this.setFromNormalAndCoplanarPoint( normal, a ); + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; - return this; + 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' ) { - clone: function () { + var ae = a * e, af = a * f, be = b * e, bf = b * f; - return new this.constructor().copy( this ); + 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; - copy: function ( plane ) { + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; - this.normal.copy( plane.normal ); - this.constant = plane.constant; + } else if ( euler.order === 'YZX' ) { - return this; + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - }, + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; - normalize: function () { + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; - // Note: will lead to a divide by zero if the plane is invalid. + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; - var inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; + } else if ( euler.order === 'XZY' ) { - return this; + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; - }, + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; - negate: function () { + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; - this.constant *= - 1; - this.normal.negate(); + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; - return this; + } - }, + // bottom row + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; - distanceToPoint: function ( point ) { + // last column + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - return this.normal.dot( point ) + this.constant; + return this; }, - distanceToSphere: function ( sphere ) { + makeRotationFromQuaternion: function () { - return this.distanceToPoint( sphere.center ) - sphere.radius; + var zero = new Vector3( 0, 0, 0 ); + var one = new Vector3( 1, 1, 1 ); - }, + return function makeRotationFromQuaternion( q ) { - projectPoint: function ( point, target ) { + return this.compose( zero, q, one ); - if ( target === undefined ) { + }; - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); + }(), - } + lookAt: function () { - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); + var x = new Vector3(); + var y = new Vector3(); + var z = new Vector3(); - }, + return function lookAt( eye, target, up ) { - intersectLine: function () { + var te = this.elements; - var v1 = new Vector3(); + z.subVectors( eye, target ); - return function intersectLine( line, target ) { + if ( z.lengthSq() === 0 ) { - if ( target === undefined ) { + // eye and target are in the same position - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); + z.z = 1; } - var direction = line.delta( v1 ); + z.normalize(); + x.crossVectors( up, z ); - var denominator = this.normal.dot( direction ); + if ( x.lengthSq() === 0 ) { - if ( denominator === 0 ) { + // up and z are parallel - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { + if ( Math.abs( up.z ) === 1 ) { - return target.copy( line.start ); + z.x += 0.0001; - } + } else { - // Unsure if this is the correct method to handle this case. - return undefined; + z.z += 0.0001; - } + } - var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + z.normalize(); + x.crossVectors( up, z ); - if ( t < 0 || t > 1 ) { + } - return undefined; + 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 target.copy( direction ).multiplyScalar( t ).add( line.start ); + return this; }; }(), - intersectsLine: function ( line ) { + multiply: function ( m, n ) { - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + if ( n !== undefined ) { - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + } + + return this.multiplyMatrices( this, m ); }, - intersectsBox: function ( box ) { + premultiply: function ( m ) { - return box.intersectsPlane( this ); + return this.multiplyMatrices( m, this ); }, - intersectsSphere: function ( sphere ) { + multiplyMatrices: function ( a, b ) { - return sphere.intersectsPlane( this ); + var ae = a.elements; + var be = b.elements; + var te = this.elements; - }, + var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - coplanarPoint: function ( target ) { + var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - if ( target === undefined ) { + 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; - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); + 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; - return target.copy( this.normal ).multiplyScalar( - this.constant ); + 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; - applyMatrix4: function () { + }, - var v1 = new Vector3(); - var m1 = new Matrix3(); + multiplyScalar: function ( s ) { - return function applyMatrix4( matrix, optionalNormalMatrix ) { + var te = this.elements; - var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); + 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; - var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); + return this; - var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + }, - this.constant = - referencePoint.dot( normal ); + applyToBufferAttribute: function () { - return this; + var v1 = new Vector3(); - }; + return function applyToBufferAttribute( attribute ) { - }(), + for ( var i = 0, l = attribute.count; i < l; i ++ ) { - translate: function ( offset ) { + v1.x = attribute.getX( i ); + v1.y = attribute.getY( i ); + v1.z = attribute.getZ( i ); - this.constant -= offset.dot( this.normal ); + v1.applyMatrix4( this ); - return this; + attribute.setXYZ( i, v1.x, v1.y, v1.z ); - }, + } - equals: function ( plane ) { + return attribute; - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); + }; - } + }(), - } ); + determinant: function () { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://clara.io - */ + var te = this.elements; - function Frustum( p0, p1, p2, p3, p4, p5 ) { + var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - this.planes = [ + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() + 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.assign( Frustum.prototype, { + transpose: function () { - set: function ( p0, p1, p2, p3, p4, p5 ) { + var te = this.elements; + var tmp; - var planes = this.planes; + 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; - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); + 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; }, - clone: function () { + setPosition: function ( v ) { - return new this.constructor().copy( this ); + var te = this.elements; + + te[ 12 ] = v.x; + te[ 13 ] = v.y; + te[ 14 ] = v.z; + + return this; }, - copy: function ( frustum ) { + getInverse: function ( m, throwOnDegenerate ) { - var planes = this.planes; + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements, + me = m.elements, - for ( var i = 0; i < 6; i ++ ) { + n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], + n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], + n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], + n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], - planes[ i ].copy( frustum.planes[ i ] ); + t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, + t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, + t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, + t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + + if ( det === 0 ) { + + var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; + + if ( throwOnDegenerate === true ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + return this.identity(); } + var detInv = 1 / det; + + te[ 0 ] = t11 * detInv; + te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; + te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; + te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; + + te[ 4 ] = t12 * detInv; + te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; + te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; + te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; + + te[ 8 ] = t13 * detInv; + te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; + te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; + te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; + + te[ 12 ] = t14 * detInv; + te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; + te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; + te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; + return this; }, - setFromMatrix: function ( m ) { + scale: function ( v ) { - var planes = this.planes; - var me = m.elements; - var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + var te = this.elements; + var x = v.x, y = v.y, z = v.z; - 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(); + 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; }, - intersectsObject: function () { + getMaxScaleOnAxis: function () { - var sphere = new Sphere(); + var te = this.elements; - return function intersectsObject( object ) { + var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - var geometry = object.geometry; + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - if ( geometry.boundingSphere === null ) - geometry.computeBoundingSphere(); + }, - sphere.copy( geometry.boundingSphere ) - .applyMatrix4( object.matrixWorld ); + makeTranslation: function ( x, y, z ) { - return this.intersectsSphere( sphere ); + this.set( - }; + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 - }(), + ); - intersectsSprite: function () { + return this; - var sphere = new Sphere(); + }, - return function intersectsSprite( sprite ) { + makeRotationX: function ( theta ) { - sphere.center.set( 0, 0, 0 ); - sphere.radius = 0.7071067811865476; - sphere.applyMatrix4( sprite.matrixWorld ); + var c = Math.cos( theta ), s = Math.sin( theta ); - return this.intersectsSphere( sphere ); + this.set( - }; + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 - }(), + ); - intersectsSphere: function ( sphere ) { + return this; - var planes = this.planes; - var center = sphere.center; - var negRadius = - sphere.radius; + }, - for ( var i = 0; i < 6; i ++ ) { + makeRotationY: function ( theta ) { - var distance = planes[ i ].distanceToPoint( center ); + var c = Math.cos( theta ), s = Math.sin( theta ); - if ( distance < negRadius ) { + this.set( - return false; + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 - } + ); - } + return this; - return true; + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; }, - intersectsBox: function () { + makeRotationAxis: function ( axis, angle ) { - var p1 = new Vector3(), - p2 = new Vector3(); + // Based on http://www.gamedev.net/reference/articles/article1199.asp - return function intersectsBox( box ) { + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; - var planes = this.planes; + this.set( - for ( var i = 0; i < 6; i ++ ) { + 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 - var plane = planes[ i ]; + ); - p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; - p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; - p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; - p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; - p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; - p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + return this; - var d1 = plane.distanceToPoint( p1 ); - var d2 = plane.distanceToPoint( p2 ); + }, - // if both outside plane, no intersection + makeScale: function ( x, y, z ) { - if ( d1 < 0 && d2 < 0 ) { + this.set( - return false; + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 - } + ); - } + return this; - return true; + }, + + makeShear: function ( x, y, z ) { + + this.set( + + 1, y, z, 0, + x, 1, z, 0, + x, y, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function ( position, quaternion, scale ) { + + var te = this.elements; + + var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + var 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: function () { + + var vector = new Vector3(); + var matrix = new Matrix4(); + + return function decompose( position, quaternion, scale ) { + + var te = this.elements; + + var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if ( det < 0 ) sx = - sx; + + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + // scale the rotation part + matrix.copy( this ); + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[ 0 ] *= invSX; + matrix.elements[ 1 ] *= invSX; + matrix.elements[ 2 ] *= invSX; + + matrix.elements[ 4 ] *= invSY; + matrix.elements[ 5 ] *= invSY; + matrix.elements[ 6 ] *= invSY; + + matrix.elements[ 8 ] *= invSZ; + matrix.elements[ 9 ] *= invSZ; + matrix.elements[ 10 ] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; }; }(), - containsPoint: function ( point ) { + makePerspective: function ( left, right, top, bottom, near, far ) { - var planes = this.planes; + if ( far === undefined ) { - for ( var i = 0; i < 6; i ++ ) { + console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); - if ( planes[ i ].distanceToPoint( point ) < 0 ) { + } - return false; + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); - } + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = 1.0 / ( right - left ); + var h = 1.0 / ( top - bottom ); + var p = 1.0 / ( far - near ); + + var x = ( right + left ) * w; + var y = ( top + bottom ) * h; + var z = ( far + near ) * p; + + te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }, + + equals: function ( matrix ) { + + var te = this.elements; + var me = matrix.elements; + + for ( var i = 0; i < 16; i ++ ) { + + if ( te[ i ] !== me[ i ] ) return false; } return true; + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + for ( var i = 0; i < 16; i ++ ) { + + this.elements[ i ] = array[ i + offset ]; + + } + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; + + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; + + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; + + return array; + } } ); - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; + 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\n"; + 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\n"; + var alphatest_fragment = "#ifdef 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( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n"; + var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - var begin_vertex = "\nvec3 transformed = vec3( position );\n"; + var begin_vertex = "vec3 transformed = vec3( position );"; - var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n"; + var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t}\n\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n"; + var 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}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\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( specularColor, dotNV );\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}"; - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n"; + var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\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"; - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif\n"; + var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n"; + var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n"; + var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvarying vec3 vViewPosition;\n#endif"; - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n"; + var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif"; var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n"; + var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; - var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\n"; + var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}"; - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n"; + var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV( sampler2D envMap, vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif"; - var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n"; + var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = normalMatrix * objectTangent;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n"; + var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; + var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).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\n"; + var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n"; + var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n"; + 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.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract(Le);\n\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n\treturn vec4( max(vRGB, 0.0), 1.0 );\n}\n"; + var 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 = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = 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 envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n"; + var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n"; + var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n"; + var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n"; + var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - var fog_vertex = "\n#ifdef USE_FOG\nfogDepth = -mvPosition.z;\n#endif"; + var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = -mvPosition.z;\n#endif"; - var fog_pars_vertex = "#ifdef USE_FOG\n varying float fogDepth;\n#endif\n"; + var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n"; + var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif\n"; + var 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 gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n"; + var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif"; - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; + var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\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 = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n"; + var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\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;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\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#endif"; - var lights_pars_begin = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n"; + var lights_pars_begin = "uniform 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\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; - var lights_pars_maps = "#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n"; + var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent ));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; + 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\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n"; + var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n"; + var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif"; - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n"; + var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#endif\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#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\tfloat clearCoatInv = 1.0 - clearCoatDHR;\n\t#if defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec3 singleScattering = vec3( 0.0 );\n\t\tvec3 multiScattering = vec3( 0.0 );\n\t\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\t\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\t\tvec3 diffuse = material.diffuseColor;\n\t\treflectedLight.indirectSpecular += clearCoatInv * radiance * singleScattering;\n\t\treflectedLight.indirectDiffuse += multiScattering * cosineWeightedIrradiance;\n\t\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n\t#else\n\t\treflectedLight.indirectSpecular += clearCoatInv * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#endif\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearCoatRadiance = vec3( 0.0 );\n#endif\n"; + var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#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\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\n\t#endif\n#endif\n"; + var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\n\t#ifndef STANDARD\n\t\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\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, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n"; + 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, irradiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n"; + var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n#endif"; - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif"; + var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif\n"; + var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif"; - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n"; + var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n"; + var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n"; + var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif"; - var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif\n"; + var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif"; - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n"; + var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; + var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n"; + var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n#endif\n"; + var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\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 * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t#endif\n#endif"; - var normal_fragment_maps = "#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; + var normal_fragment_maps = "#ifdef USE_NORMALMAP\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t#ifdef FLIP_SIDED\n\t\t\tnormal = - normal;\n\t\t#endif\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\tnormal = normalize( normalMatrix * normal );\n\t#else\n\t\t#ifdef USE_TANGENT\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t\tmapN.xy = normalScale * mapN.xy;\n\t\t\tnormal = normalize( vTBN * mapN );\n\t\t#else\n\t\t\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\t\t#endif\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\t\tscale *= float( gl_FrontFacing ) * 2.0 - 1.0;\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n"; + var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\t#ifdef OBJECTSPACE_NORMALMAP\n\t\tuniform mat3 normalMatrix;\n\t#else\n\t\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\t\tvec2 st0 = dFdx( vUv.st );\n\t\t\tvec2 st1 = dFdy( vUv.st );\n\t\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\t\tvec3 N = normalize( surf_norm );\n\t\t\tmat3 tsn = mat3( S, T, N );\n\t\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\t\tmapN.xy *= normalScale;\n\t\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\treturn normalize( tsn * mapN );\n\t\t}\n\t#endif\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}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n"; + var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n"; + var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n"; + var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;"; - var dithering_fragment = "#if defined( DITHERING )\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif\n"; + var dithering_fragment = "#if defined( DITHERING )\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif\n"; + var dithering_pars_fragment = "#if defined( DITHERING )\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n"; + var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n"; + var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif\n"; + var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n#endif"; - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif\n"; + var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif"; - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}\n"; + var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}"; var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n"; + var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif\n"; + var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; + var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n"; + var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n"; + var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( ( color * ( 2.51 * color + 0.03 ) ) / ( color * ( 2.43 * color + 0.59 ) + 0.14 ) );\n}"; var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif"; - var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\n"; + var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif"; var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; @@ -6080,55 +6180,67 @@ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif"; - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n"; + var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\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}"; + + 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}"; + + var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldDirection;\nvoid main() {\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n"; + var cube_vert = "varying vec3 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 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}\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 \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\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\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 \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\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n"; + var depth_vert = "#include \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}"; - var depth_vert = "#include \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}\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}"; - 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}\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}"; - 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}\n"; + var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n"; + var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - var equirect_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\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}"; - 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}\n"; + var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\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 \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}\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 \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\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * 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}"; - 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 \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\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * 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}\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}"; - 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}\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 \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\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\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}"; - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \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\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\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}\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}"; - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\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 \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}\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 \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\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}"; - 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}\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 \nvoid main() {\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}"; - 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}\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 \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}"; - var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \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\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\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}"; - var meshphysical_vert = "#define PHYSICAL\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}\n"; + var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\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 \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\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\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( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}\n"; + var meshphysical_vert = "#define PHYSICAL\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}"; - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \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#endif\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( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n"; + var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_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 \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; - var points_frag = "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\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; + var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_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 \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#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || ( defined( USE_NORMALMAP ) && ! defined( OBJECTSPACE_NORMALMAP ) )\n\tvViewPosition = - mvPosition.xyz;\n#endif\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#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\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}"; - 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}\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 = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\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 shadow_vert = "#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\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}"; + + var shadow_vert = "#include \n#include \nvoid main() {\n\t#include \n\t#include \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 \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\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\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 = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\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 ShaderChunk = { alphamap_fragment: alphamap_fragment, @@ -6160,6 +6272,7 @@ envmap_fragment: envmap_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, + envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, @@ -6170,7 +6283,6 @@ lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars_begin: lights_pars_begin, - lights_pars_maps: lights_pars_maps, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, @@ -6221,6 +6333,8 @@ 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, @@ -6235,6 +6349,8 @@ meshbasic_vert: meshbasic_vert, meshlambert_frag: meshlambert_frag, meshlambert_vert: meshlambert_vert, + meshmatcap_frag: meshmatcap_frag, + meshmatcap_vert: meshmatcap_vert, meshphong_frag: meshphong_frag, meshphong_vert: meshphong_vert, meshphysical_frag: meshphysical_frag, @@ -6244,73 +6360,75 @@ points_frag: points_frag, points_vert: points_vert, shadow_frag: shadow_frag, - shadow_vert: shadow_vert + shadow_vert: shadow_vert, + sprite_frag: sprite_frag, + sprite_vert: sprite_vert }; /** * Uniform Utilities */ - var UniformsUtils = { + function cloneUniforms( src ) { - merge: function ( uniforms ) { + var dst = {}; - var merged = {}; + for ( var u in src ) { - for ( var u = 0; u < uniforms.length; u ++ ) { + dst[ u ] = {}; - var tmp = this.clone( uniforms[ u ] ); + for ( var p in src[ u ] ) { - for ( var p in tmp ) { + var property = src[ u ][ p ]; - merged[ p ] = tmp[ p ]; + if ( property && ( property.isColor || + property.isMatrix3 || property.isMatrix4 || + property.isVector2 || property.isVector3 || property.isVector4 || + property.isTexture ) ) { - } + dst[ u ][ p ] = property.clone(); - } + } else if ( Array.isArray( property ) ) { - return merged; + dst[ u ][ p ] = property.slice(); - }, + } else { - clone: function ( uniforms_src ) { + dst[ u ][ p ] = property; - var uniforms_dst = {}; + } - for ( var u in uniforms_src ) { + } - uniforms_dst[ u ] = {}; + } - for ( var p in uniforms_src[ u ] ) { + return dst; - var parameter_src = uniforms_src[ u ][ p ]; + } - if ( parameter_src && ( parameter_src.isColor || - parameter_src.isMatrix3 || parameter_src.isMatrix4 || - parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || - parameter_src.isTexture ) ) { + function mergeUniforms( uniforms ) { - uniforms_dst[ u ][ p ] = parameter_src.clone(); + var merged = {}; - } else if ( Array.isArray( parameter_src ) ) { + for ( var u = 0; u < uniforms.length; u ++ ) { - uniforms_dst[ u ][ p ] = parameter_src.slice(); + var tmp = cloneUniforms( uniforms[ u ] ); - } else { + for ( var p in tmp ) { - uniforms_dst[ u ][ p ] = parameter_src; + merged[ p ] = tmp[ p ]; - } + } - } + } - } + return merged; - return uniforms_dst; + } - } + // Legacy - }; + var UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; /** * @author mrdoob / http://mrdoob.com/ @@ -6625,23 +6743,73 @@ }, - convertGammaToLinear: function () { + convertGammaToLinear: function ( gammaFactor ) { - var r = this.r, g = this.g, b = this.b; + this.copyGammaToLinear( this, gammaFactor ); + + return this; + + }, + + convertLinearToGamma: function ( gammaFactor ) { + + this.copyLinearToGamma( this, gammaFactor ); + + return this; + + }, + + copySRGBToLinear: function () { - this.r = r * r; - this.g = g * g; - this.b = b * b; + function SRGBToLinear( c ) { + + return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); + + } + + return function copySRGBToLinear( color ) { + + this.r = SRGBToLinear( color.r ); + this.g = SRGBToLinear( color.g ); + this.b = SRGBToLinear( color.b ); + + return this; + + }; + + }(), + + copyLinearToSRGB: function () { + + function LinearToSRGB( c ) { + + return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; + + } + + return function copyLinearToSRGB( color ) { + + this.r = LinearToSRGB( color.r ); + this.g = LinearToSRGB( color.g ); + this.b = LinearToSRGB( color.b ); + + return this; + + }; + + }(), + + convertSRGBToLinear: function () { + + this.copySRGBToLinear( this ); return this; }, - convertLinearToGamma: function () { + convertLinearToSRGB: function () { - this.r = Math.sqrt( this.r ); - this.g = Math.sqrt( this.g ); - this.b = Math.sqrt( this.b ); + this.copyLinearToSRGB( this ); return this; @@ -6803,17 +6971,39 @@ }, - equals: function ( c ) { + lerpHSL: function () { - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + var hslA = { h: 0, s: 0, l: 0 }; + var hslB = { h: 0, s: 0, l: 0 }; - }, + return function lerpHSL( color, alpha ) { - fromArray: function ( array, offset ) { + this.getHSL( hslA ); + color.getHSL( hslB ); - if ( offset === undefined ) offset = 0; + var h = _Math.lerp( hslA.h, hslB.h, alpha ); + var s = _Math.lerp( hslA.s, hslB.s, alpha ); + var l = _Math.lerp( hslA.l, hslB.l, alpha ); - this.r = array[ offset ]; + this.setHSL( h, s, l ); + + return this; + + }; + + }(), + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; @@ -6949,6 +7139,8 @@ ambientLightColor: { value: [] }, + lightProbe: { value: [] }, + directionalLights: { value: [], properties: { direction: {}, color: {}, @@ -7022,6 +7214,17 @@ map: { value: null }, uvTransform: { value: new Matrix3() } + }, + + sprite: { + + diffuse: { value: new Color( 0xeeeeee ) }, + opacity: { value: 1.0 }, + center: { value: new Vector2( 0.5, 0.5 ) }, + rotation: { value: 0.0 }, + map: { value: null }, + uvTransform: { value: new Matrix3() } + } }; @@ -7036,7 +7239,7 @@ basic: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, @@ -7052,7 +7255,7 @@ lambert: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, @@ -7073,7 +7276,7 @@ phong: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, @@ -7100,7 +7303,7 @@ standard: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, @@ -7126,9 +7329,27 @@ }, + matcap: { + + uniforms: mergeUniforms( [ + UniformsLib.common, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.fog, + { + matcap: { value: null } + } + ] ), + + vertexShader: ShaderChunk.meshmatcap_vert, + fragmentShader: ShaderChunk.meshmatcap_frag + + }, + points: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.points, UniformsLib.fog ] ), @@ -7140,7 +7361,7 @@ dashed: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.fog, { @@ -7157,7 +7378,7 @@ depth: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap ] ), @@ -7169,7 +7390,7 @@ normal: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, @@ -7184,6 +7405,29 @@ }, + sprite: { + + uniforms: mergeUniforms( [ + UniformsLib.sprite, + UniformsLib.fog + ] ), + + vertexShader: ShaderChunk.sprite_vert, + fragmentShader: ShaderChunk.sprite_frag + + }, + + background: { + + uniforms: { + uvTransform: { value: new Matrix3() }, + t2D: { value: null }, + }, + + vertexShader: ShaderChunk.background_vert, + fragmentShader: ShaderChunk.background_frag + + }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ @@ -7214,7 +7458,7 @@ distanceRGBA: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.common, UniformsLib.displacementmap, { @@ -7231,7 +7475,7 @@ shadow: { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ UniformsLib.lights, UniformsLib.fog, { @@ -7249,7 +7493,7 @@ ShaderLib.physical = { - uniforms: UniformsUtils.merge( [ + uniforms: mergeUniforms( [ ShaderLib.standard.uniforms, { clearCoat: { value: 0 }, @@ -7262,6 +7506,61 @@ }; + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function WebGLAnimation() { + + var context = null; + var isAnimating = false; + var animationLoop = null; + + function onAnimationFrame( time, frame ) { + + if ( isAnimating === false ) return; + + animationLoop( time, frame ); + + context.requestAnimationFrame( onAnimationFrame ); + + } + + return { + + start: function () { + + if ( isAnimating === true ) return; + if ( animationLoop === null ) return; + + context.requestAnimationFrame( onAnimationFrame ); + + isAnimating = true; + + }, + + stop: function () { + + isAnimating = false; + + }, + + setAnimationLoop: function ( callback ) { + + animationLoop = callback; + + }, + + setContext: function ( value ) { + + context = value; + + } + + }; + + } + /** * @author mrdoob / http://mrdoob.com/ */ @@ -7273,7 +7572,7 @@ function createBuffer( attribute, bufferType ) { var array = attribute.array; - var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; + var usage = attribute.dynamic ? 35048 : 35044; var buffer = gl.createBuffer(); @@ -7282,11 +7581,11 @@ attribute.onUploadCallback(); - var type = gl.FLOAT; + var type = 5126; if ( array instanceof Float32Array ) { - type = gl.FLOAT; + type = 5126; } else if ( array instanceof Float64Array ) { @@ -7294,27 +7593,27 @@ } else if ( array instanceof Uint16Array ) { - type = gl.UNSIGNED_SHORT; + type = 5123; } else if ( array instanceof Int16Array ) { - type = gl.SHORT; + type = 5122; } else if ( array instanceof Uint32Array ) { - type = gl.UNSIGNED_INT; + type = 5125; } else if ( array instanceof Int32Array ) { - type = gl.INT; + type = 5124; } else if ( array instanceof Int8Array ) { - type = gl.BYTE; + type = 5120; } else if ( array instanceof Uint8Array ) { - type = gl.UNSIGNED_BYTE; + type = 5121; } @@ -7336,7 +7635,7 @@ if ( attribute.dynamic === false ) { - gl.bufferData( bufferType, array, gl.STATIC_DRAW ); + gl.bufferData( bufferType, array, 35044 ); } else if ( updateRange.count === - 1 ) { @@ -7415,6 +7714,64 @@ } + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + + function Face3( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); + this.vertexNormals = Array.isArray( normal ) ? normal : []; + + this.color = ( color && color.isColor ) ? color : new Color(); + this.vertexColors = Array.isArray( color ) ? color : []; + + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + + } + + Object.assign( Face3.prototype, { + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( source ) { + + this.a = source.a; + this.b = source.b; + this.c = source.c; + + this.normal.copy( source.normal ); + this.color.copy( source.color ); + + this.materialIndex = source.materialIndex; + + for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + + this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + + } + + for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { + + this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + + } + + return this; + + } + + } ); + /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley @@ -7850,18 +8207,22 @@ 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 }, @@ -7906,7 +8267,9 @@ applyMatrix: function ( matrix ) { - this.matrix.multiplyMatrices( matrix, this.matrix ); + if ( this.matrixAutoUpdate ) this.updateMatrix(); + + this.matrix.premultiply( matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); @@ -8100,35 +8463,51 @@ lookAt: function () { - // This method does not support objects with rotated and/or translated parent(s) + // This method does not support objects having non-uniformly-scaled parent(s) + var q1 = new Quaternion(); var m1 = new Matrix4(); - var vector = new Vector3(); + var target = new Vector3(); + var position = new Vector3(); return function lookAt( x, y, z ) { if ( x.isVector3 ) { - vector.copy( x ); + target.copy( x ); } else { - vector.set( x, y, z ); + target.set( x, y, z ); } - if ( this.isCamera ) { + var parent = this.parent; - m1.lookAt( this.position, vector, this.up ); + this.updateWorldMatrix( true, false ); + + position.setFromMatrixPosition( this.matrixWorld ); + + if ( this.isCamera || this.isLight ) { + + m1.lookAt( position, target, this.up ); } else { - m1.lookAt( vector, this.position, this.up ); + m1.lookAt( target, position, this.up ); } this.quaternion.setFromRotationMatrix( m1 ); + if ( parent ) { + + m1.extractRotation( parent.matrixWorld ); + q1.setFromRotationMatrix( m1 ); + this.quaternion.premultiply( q1.inverse() ); + + } + }; }(), @@ -8303,26 +8682,22 @@ }(), - getWorldDirection: function () { - - var quaternion = new Quaternion(); - - return function getWorldDirection( target ) { + getWorldDirection: function ( target ) { - if ( target === undefined ) { + if ( target === undefined ) { - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); + console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); + target = new Vector3(); - } + } - this.getWorldQuaternion( quaternion ); + this.updateMatrixWorld( true ); - return target.set( 0, 0, 1 ).applyQuaternion( quaternion ); + var e = this.matrixWorld.elements; - }; + return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - }(), + }, raycast: function () {}, @@ -8412,6 +8787,44 @@ }, + updateWorldMatrix: function ( updateParents, updateChildren ) { + + var parent = this.parent; + + if ( updateParents === true && parent !== null ) { + + parent.updateWorldMatrix( true, false ); + + } + + if ( this.matrixAutoUpdate ) this.updateMatrix(); + + if ( this.parent === null ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + // update children + + if ( updateChildren === true ) { + + var children = this.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].updateWorldMatrix( false, true ); + + } + + } + + }, + toJSON: function ( meta ) { // meta is a string when called from JSON.stringify @@ -8456,10 +8869,15 @@ 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(); if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; + // object specific properties + + if ( this.isMesh && this.drawMode !== TrianglesDrawMode ) object.drawMode = this.drawMode; + // function serialize( library, element ) { @@ -8474,7 +8892,7 @@ } - if ( this.geometry !== undefined ) { + if ( this.isMesh || this.isLine || this.isPoints ) { object.geometry = serialize( meta.geometries, this.geometry ); @@ -8631,275 +9049,6 @@ } ); - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Camera() { - - Object3D.call( this ); - - this.type = 'Camera'; - - this.matrixWorldInverse = new Matrix4(); - this.projectionMatrix = new Matrix4(); - - } - - Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Camera, - - isCamera: true, - - copy: function ( source, recursive ) { - - Object3D.prototype.copy.call( this, source, recursive ); - - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - this.projectionMatrix.copy( source.projectionMatrix ); - - return this; - - }, - - getWorldDirection: function () { - - var quaternion = new Quaternion(); - - return function getWorldDirection( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); - - } - - this.getWorldQuaternion( quaternion ); - - return target.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - - }; - - }(), - - updateMatrixWorld: function ( force ) { - - Object3D.prototype.updateMatrixWorld.call( this, force ); - - this.matrixWorldInverse.getInverse( this.matrixWorld ); - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - } - - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - * @author arose / http://github.com/arose - */ - - function OrthographicCamera( left, right, top, bottom, near, far ) { - - Camera.call( this ); - - this.type = 'OrthographicCamera'; - - this.zoom = 1; - this.view = null; - - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; - - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; - - this.updateProjectionMatrix(); - - } - - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { - - constructor: OrthographicCamera, - - isOrthographicCamera: true, - - copy: function ( source, recursive ) { - - Camera.prototype.copy.call( this, 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: function ( 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: function () { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - }, - - updateProjectionMatrix: function () { - - var dx = ( this.right - this.left ) / ( 2 * this.zoom ); - var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - var cx = ( this.right + this.left ) / 2; - var cy = ( this.top + this.bottom ) / 2; - - var left = cx - dx; - var right = cx + dx; - var top = cy + dy; - var bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); - var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); - var scaleW = ( this.right - this.left ) / this.view.width; - var scaleH = ( this.top - this.bottom ) / this.view.height; - - left += scaleW * ( this.view.offsetX / zoomW ); - right = left + scaleW * ( this.view.width / zoomW ); - top -= scaleH * ( this.view.offsetY / zoomH ); - bottom = top - scaleH * ( this.view.height / zoomH ); - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - - }, - - toJSON: function ( meta ) { - - var data = Object3D.prototype.toJSON.call( this, 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; - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - - function Face3( a, b, c, normal, color, materialIndex ) { - - this.a = a; - this.b = b; - this.c = c; - - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; - - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; - - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - - } - - Object.assign( Face3.prototype, { - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( source ) { - - this.a = source.a; - this.b = source.b; - this.c = source.c; - - this.normal.copy( source.normal ); - this.color.copy( source.color ); - - this.materialIndex = source.materialIndex; - - for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); - - } - - for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { - - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - - } - - return this; - - } - - } ); - /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ @@ -9118,35 +9267,13 @@ if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; - var tempNormals = []; - var tempUVs = []; - var tempUVs2 = []; - for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { - scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); - - if ( normals !== undefined ) { - - tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); - - } + scope.vertices.push( new Vector3().fromArray( positions, i ) ); if ( colors !== undefined ) { - scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); - - } - - if ( uvs !== undefined ) { - - tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); - - } - - if ( uvs2 !== undefined ) { - - tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); + scope.colors.push( new Color().fromArray( colors, i ) ); } @@ -9154,8 +9281,16 @@ function addFace( a, b, c, materialIndex ) { - var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; - var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; + var vertexColors = ( colors === undefined ) ? [] : [ + scope.colors[ a ].clone(), + scope.colors[ b ].clone(), + scope.colors[ c ].clone() ]; + + var vertexNormals = ( normals === undefined ) ? [] : [ + new Vector3().fromArray( normals, a * 3 ), + new Vector3().fromArray( normals, b * 3 ), + new Vector3().fromArray( normals, c * 3 ) + ]; var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); @@ -9163,13 +9298,21 @@ if ( uvs !== undefined ) { - scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); + scope.faceVertexUvs[ 0 ].push( [ + new Vector2().fromArray( uvs, a * 2 ), + new Vector2().fromArray( uvs, b * 2 ), + new Vector2().fromArray( uvs, c * 2 ) + ] ); } if ( uvs2 !== undefined ) { - scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); + scope.faceVertexUvs[ 1 ].push( [ + new Vector2().fromArray( uvs2, a * 2 ), + new Vector2().fromArray( uvs2, b * 2 ), + new Vector2().fromArray( uvs2, c * 2 ) + ] ); } @@ -10841,7 +10984,10 @@ for ( var i = 0; i < morphTargetsLength; i ++ ) { - morphTargetsPosition[ i ] = []; + morphTargetsPosition[ i ] = { + name: morphTargets[ i ].name, + data: [] + }; } @@ -10860,7 +11006,10 @@ for ( var i = 0; i < morphNormalsLength; i ++ ) { - morphTargetsNormal[ i ] = []; + morphTargetsNormal[ i ] = { + name: morphNormals[ i ].name, + data: [] + }; } @@ -10878,6 +11027,12 @@ // + if ( vertices.length > 0 && faces.length === 0 ) { + + console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); + + } + for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; @@ -10954,7 +11109,7 @@ var morphTarget = morphTargets[ j ].vertices; - morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); } @@ -10962,7 +11117,7 @@ var morphNormal = morphNormals[ j ].vertexNormals[ i ]; - morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); + morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); } @@ -11044,6 +11199,8 @@ this.drawRange = { start: 0, count: Infinity }; + this.userData = {}; + } BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { @@ -11078,9 +11235,7 @@ console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - - return; + return this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); } @@ -11089,7 +11244,7 @@ console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); this.setIndex( attribute ); - return; + return this; } @@ -11160,6 +11315,18 @@ } + var tangent = this.attributes.tangent; + + if ( tangent !== undefined ) { + + var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + + // Tangent is vec4, but the '.w' component is a sign value (+1/-1). + normalMatrix.applyToBufferAttribute( tangent ); + tangent.needsUpdate = true; + + } + if ( this.boundingBox !== null ) { this.computeBoundingBox(); @@ -11548,9 +11715,10 @@ var morphTarget = morphTargets[ i ]; - var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); + var attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 ); + attribute.name = morphTarget.name; - array.push( attribute.copyVector3sArray( morphTarget ) ); + array.push( attribute.copyVector3sArray( morphTarget.data ) ); } @@ -11594,35 +11762,59 @@ computeBoundingBox: function () { - if ( this.boundingBox === null ) { + var box = new Box3(); - this.boundingBox = new Box3(); + return function computeBoundingBox() { - } + if ( this.boundingBox === null ) { - var position = this.attributes.position; + this.boundingBox = new Box3(); - if ( position !== undefined ) { + } - this.boundingBox.setFromBufferAttribute( position ); + var position = this.attributes.position; + var morphAttributesPosition = this.morphAttributes.position; - } else { + if ( position !== undefined ) { - this.boundingBox.makeEmpty(); + this.boundingBox.setFromBufferAttribute( position ); - } + // process morph attributes if present - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + if ( morphAttributesPosition ) { - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - } + var morphAttribute = morphAttributesPosition[ i ]; + box.setFromBufferAttribute( morphAttribute ); - }, + this.boundingBox.expandByPoint( box.min ); + this.boundingBox.expandByPoint( box.max ); + + } + + } + + } else { + + this.boundingBox.makeEmpty(); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + + } + + }; + + }(), computeBoundingSphere: function () { var box = new Box3(); + var boxMorphTargets = new Box3(); var vector = new Vector3(); return function computeBoundingSphere() { @@ -11634,28 +11826,67 @@ } var position = this.attributes.position; + var morphAttributesPosition = this.morphAttributes.position; if ( position ) { + // first, find the center of the bounding sphere + var center = this.boundingSphere.center; box.setFromBufferAttribute( position ); + + // process morph attributes if present + + if ( morphAttributesPosition ) { + + for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { + + var morphAttribute = morphAttributesPosition[ i ]; + boxMorphTargets.setFromBufferAttribute( morphAttribute ); + + box.expandByPoint( boxMorphTargets.min ); + box.expandByPoint( boxMorphTargets.max ); + + } + + } + box.getCenter( center ); - // hoping to find a boundingSphere with a radius smaller than the + // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case var maxRadiusSq = 0; for ( var i = 0, il = position.count; i < il; i ++ ) { - vector.x = position.getX( i ); - vector.y = position.getY( i ); - vector.z = position.getZ( i ); + vector.fromBufferAttribute( position, i ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } + // process morph attributes if present + + if ( morphAttributesPosition ) { + + for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { + + var morphAttribute = morphAttributesPosition[ i ]; + + for ( var j = 0, jl = morphAttribute.count; j < jl; j ++ ) { + + vector.fromBufferAttribute( morphAttribute, j ); + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + } + + } + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { @@ -11680,7 +11911,6 @@ var index = this.index; var attributes = this.attributes; - var groups = this.groups; if ( attributes.position ) { @@ -11716,46 +11946,31 @@ var indices = index.array; - if ( groups.length === 0 ) { - - this.addGroup( 0, indices.length ); - - } - - for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + for ( var i = 0, il = index.count; i < il; i += 3 ) { - var group = groups[ j ]; + vA = indices[ i + 0 ] * 3; + vB = indices[ i + 1 ] * 3; + vC = indices[ i + 2 ] * 3; - var start = group.start; - var count = group.count; + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); - for ( var i = start, il = start + count; i < il; i += 3 ) { - - vA = indices[ i + 0 ] * 3; - vB = indices[ i + 1 ] * 3; - vC = indices[ i + 2 ] * 3; - - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); - - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); - - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; - } + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; } @@ -11829,9 +12044,10 @@ var attribute2 = geometry.attributes[ key ]; var attributeArray2 = attribute2.array; - var attributeSize = attribute2.itemSize; + var attributeOffset = attribute2.itemSize * offset; + var length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); - for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + for ( var i = 0, j = attributeOffset; i < length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; @@ -11869,6 +12085,33 @@ toNonIndexed: function () { + function convertBufferAttribute( attribute, indices ) { + + var array = attribute.array; + var itemSize = attribute.itemSize; + + var array2 = new array.constructor( indices.length * itemSize ); + + var index = 0, index2 = 0; + + for ( var i = 0, l = indices.length; i < l; i ++ ) { + + index = indices[ i ] * itemSize; + + for ( var j = 0; j < itemSize; j ++ ) { + + array2[ index2 ++ ] = array[ index ++ ]; + + } + + } + + return new BufferAttribute( array2, itemSize ); + + } + + // + if ( this.index === null ) { console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); @@ -11881,33 +12124,43 @@ var indices = this.index.array; var attributes = this.attributes; + // attributes + for ( var name in attributes ) { var attribute = attributes[ name ]; - var array = attribute.array; - var itemSize = attribute.itemSize; + var newAttribute = convertBufferAttribute( attribute, indices ); - var array2 = new array.constructor( indices.length * itemSize ); + geometry2.addAttribute( name, newAttribute ); - var index = 0, index2 = 0; + } - for ( var i = 0, l = indices.length; i < l; i ++ ) { + // morph attributes - index = indices[ i ] * itemSize; + var morphAttributes = this.morphAttributes; - for ( var j = 0; j < itemSize; j ++ ) { + for ( name in morphAttributes ) { - array2[ index2 ++ ] = array[ index ++ ]; + var morphArray = []; + var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - } + for ( var i = 0, il = morphAttribute.length; i < il; i ++ ) { + + var attribute = morphAttribute[ i ]; + + var newAttribute = convertBufferAttribute( attribute, indices ); + + morphArray.push( newAttribute ); } - geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); + geometry2.morphAttributes[ name ] = morphArray; } + // groups + var groups = this.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { @@ -11936,6 +12189,7 @@ data.uuid = this.uuid; data.type = this.type; if ( this.name !== '' ) data.name = this.name; + if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; if ( this.parameters !== undefined ) { @@ -11957,11 +12211,9 @@ if ( index !== null ) { - var array = Array.prototype.slice.call( index.array ); - data.data.index = { type: index.array.constructor.name, - array: array + array: Array.prototype.slice.call( index.array ) }; } @@ -11972,17 +12224,57 @@ var attribute = attributes[ key ]; - var array = Array.prototype.slice.call( attribute.array ); - - data.data.attributes[ key ] = { + var attributeData = { itemSize: attribute.itemSize, type: attribute.array.constructor.name, - array: array, + array: Array.prototype.slice.call( attribute.array ), normalized: attribute.normalized }; + if ( attribute.name !== '' ) attributeData.name = attribute.name; + + data.data.attributes[ key ] = attributeData; + + } + + var morphAttributes = {}; + var hasMorphAttributes = false; + + for ( var key in this.morphAttributes ) { + + var attributeArray = this.morphAttributes[ key ]; + + var array = []; + + for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { + + var attribute = attributeArray[ i ]; + + var attributeData = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: Array.prototype.slice.call( attribute.array ), + normalized: attribute.normalized + }; + + if ( attribute.name !== '' ) attributeData.name = attribute.name; + + array.push( attributeData ); + + } + + if ( array.length > 0 ) { + + morphAttributes[ key ] = array; + + hasMorphAttributes = true; + + } + } + if ( hasMorphAttributes ) data.data.morphAttributes = morphAttributes; + var groups = this.groups; if ( groups.length > 0 ) { @@ -12129,6 +12421,10 @@ this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; + // user data + + this.userData = source.userData; + return this; }, @@ -12479,6 +12775,7 @@ this.blending = NormalBlending; this.side = FrontSide; this.flatShading = false; + this.vertexTangents = false; this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors this.opacity = 1; @@ -12514,8 +12811,6 @@ this.alphaTest = 0; this.premultipliedAlpha = false; - this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer - this.visible = true; this.userData = {}; @@ -12573,11 +12868,6 @@ currentValue.copy( newValue ); - } else if ( key === 'overdraw' ) { - - // ensure overdraw is backwards-compatible with legacy boolean type - this[ key ] = Number( newValue ); - } else { this[ key ] = newValue; @@ -12629,20 +12919,32 @@ if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; 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; + + 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; @@ -12650,6 +12952,7 @@ 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; @@ -12661,6 +12964,9 @@ data.envMap = this.envMap.toJSON( meta ).uuid; data.reflectivity = this.reflectivity; // Scale behind envMap + if ( this.combine !== undefined ) data.combine = this.combine; + if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + } if ( this.gradientMap && this.gradientMap.isTexture ) { @@ -12687,6 +12993,10 @@ // rotation (SpriteMaterial) if ( 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 !== 1 ) data.linewidth = this.linewidth; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; @@ -12785,8 +13095,6 @@ this.alphaTest = source.alphaTest; this.premultipliedAlpha = source.premultipliedAlpha; - this.overdraw = source.overdraw; - this.visible = source.visible; this.userData = JSON.parse( JSON.stringify( source.userData ) ); @@ -12822,119 +13130,9 @@ } ); - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * 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: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - 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'; + var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; - this.skinning = false; - this.morphTargets = false; - - this.lights = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, 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; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; + var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; /** * @author alteredq / http://alteredqualia.com/ @@ -12966,8 +13164,8 @@ this.defines = {}; this.uniforms = {}; - this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; - this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + this.vertexShader = default_vertex; + this.fragmentShader = default_fragment; this.linewidth = 1; @@ -13026,7 +13224,7 @@ this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; - this.uniforms = UniformsUtils.clone( source.uniforms ); + this.uniforms = cloneUniforms( source.uniforms ); this.defines = Object.assign( {}, source.defines ); @@ -13051,10 +13249,89 @@ var data = Material.prototype.toJSON.call( this, meta ); - data.uniforms = this.uniforms; + data.uniforms = {}; + + for ( var name in this.uniforms ) { + + var uniform = this.uniforms[ name ]; + var value = uniform.value; + + if ( value && value.isTexture ) { + + data.uniforms[ name ] = { + type: 't', + value: value.toJSON( meta ).uuid + }; + + } else if ( value && value.isColor ) { + + data.uniforms[ name ] = { + type: 'c', + value: value.getHex() + }; + + } else if ( value && value.isVector2 ) { + + data.uniforms[ name ] = { + type: 'v2', + value: value.toArray() + }; + + } else if ( value && value.isVector3 ) { + + data.uniforms[ name ] = { + type: 'v3', + value: value.toArray() + }; + + } else if ( value && value.isVector4 ) { + + data.uniforms[ name ] = { + type: 'v4', + value: value.toArray() + }; + + } else if ( value && value.isMatrix3 ) { + + data.uniforms[ name ] = { + type: 'm3', + value: value.toArray() + }; + + } else if ( value && value.isMatrix4 ) { + + data.uniforms[ name ] = { + type: 'm4', + value: value.toArray() + }; + + } else { + + data.uniforms[ name ] = { + value: value + }; + + // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far + + } + + } + + if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; + data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; + var extensions = {}; + + for ( var key in this.extensions ) { + + if ( this.extensions[ key ] === true ) extensions[ key ] = true; + + } + + if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; + return data; }; @@ -13349,7 +13626,7 @@ intersectsSphere: function ( sphere ) { - return this.distanceToPoint( sphere.center ) <= sphere.radius; + return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); }, @@ -13601,153 +13878,6 @@ } ); - /** - * @author bhouston / http://clara.io - */ - - function Line3( start, end ) { - - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); - - } - - Object.assign( Line3.prototype, { - - set: function ( start, end ) { - - this.start.copy( start ); - this.end.copy( end ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - copy: function ( line ) { - - this.start.copy( line.start ); - this.end.copy( line.end ); - - return this; - - }, - - getCenter: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .getCenter() target is now required' ); - target = new Vector3(); - - } - - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - - }, - - delta: function ( target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .delta() target is now required' ); - target = new Vector3(); - - } - - return target.subVectors( this.end, this.start ); - - }, - - distanceSq: function () { - - return this.start.distanceToSquared( this.end ); - - }, - - distance: function () { - - return this.start.distanceTo( this.end ); - - }, - - at: function ( t, target ) { - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .at() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - }, - - closestPointToPointParameter: function () { - - var startP = new Vector3(); - var startEnd = new Vector3(); - - return function closestPointToPointParameter( point, clampToLine ) { - - startP.subVectors( point, this.start ); - startEnd.subVectors( this.end, this.start ); - - var startEnd2 = startEnd.dot( startEnd ); - var startEnd_startP = startEnd.dot( startP ); - - var t = startEnd_startP / startEnd2; - - if ( clampToLine ) { - - t = _Math.clamp( t, 0, 1 ); - - } - - return t; - - }; - - }(), - - closestPointToPoint: function ( point, clampToLine, target ) { - - var t = this.closestPointToPointParameter( point, clampToLine ); - - if ( target === undefined ) { - - console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); - target = new Vector3(); - - } - - return this.delta( target ).multiplyScalar( t ).add( this.start ); - - }, - - applyMatrix4: function ( matrix ) { - - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); - - return this; - - }, - - equals: function ( line ) { - - return line.start.equals( this.start ) && line.end.equals( this.end ); - - } - - } ); - /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ @@ -13854,6 +13984,25 @@ }; + }(), + + getUV: function () { + + var barycoord = new Vector3(); + + return function getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { + + this.getBarycoord( point, p1, p2, p3, barycoord ); + + target.set( 0, 0 ); + target.addScaledVector( uv1, barycoord.x ); + target.addScaledVector( uv2, barycoord.y ); + target.addScaledVector( uv3, barycoord.z ); + + return target; + + }; + }() } ); @@ -13956,6 +14105,12 @@ }, + getUV: function ( point, uv1, uv2, uv3, result ) { + + return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, result ); + + }, + intersectsBox: function ( box ) { return box.intersectsTriangle( this ); @@ -13964,12 +14119,14 @@ closestPointToPoint: function () { - var plane = new Plane(); - var edgeList = [ new Line3(), new Line3(), new Line3() ]; - var projectedPoint = new Vector3(); - var closestPoint = new Vector3(); + var vab = new Vector3(); + var vac = new Vector3(); + var vbc = new Vector3(); + var vap = new Vector3(); + var vbp = new Vector3(); + var vcp = new Vector3(); - return function closestPointToPoint( point, target ) { + return function closestPointToPoint( p, target ) { if ( target === undefined ) { @@ -13978,48 +14135,81 @@ } - var minDistance = Infinity; + var a = this.a, b = this.b, c = this.c; + var v, w; + + // 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. - // project the point onto the plane of the triangle + vab.subVectors( b, a ); + vac.subVectors( c, a ); + vap.subVectors( p, a ); + var d1 = vab.dot( vap ); + var d2 = vac.dot( vap ); + if ( d1 <= 0 && d2 <= 0 ) { - plane.setFromCoplanarPoints( this.a, this.b, this.c ); - plane.projectPoint( point, projectedPoint ); + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy( a ); - // check if the projection lies within the triangle + } - if ( this.containsPoint( projectedPoint ) === true ) { + vbp.subVectors( p, b ); + var d3 = vab.dot( vbp ); + var d4 = vac.dot( vbp ); + if ( d3 >= 0 && d4 <= d3 ) { - // if so, this is the closest point + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy( b ); - target.copy( projectedPoint ); + } - } else { + var vc = d1 * d4 - d3 * d2; + if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { - // if not, the point falls outside the triangle. the target is the closest point to the triangle's edges or vertices + v = d1 / ( d1 - d3 ); + // edge region of AB; barycentric coords (1-v, v, 0) + return target.copy( a ).addScaledVector( vab, v ); - edgeList[ 0 ].set( this.a, this.b ); - edgeList[ 1 ].set( this.b, this.c ); - edgeList[ 2 ].set( this.c, this.a ); + } - for ( var i = 0; i < edgeList.length; i ++ ) { + vcp.subVectors( p, c ); + var d5 = vab.dot( vcp ); + var d6 = vac.dot( vcp ); + if ( d6 >= 0 && d5 <= d6 ) { - edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy( c ); - var distance = projectedPoint.distanceToSquared( closestPoint ); + } - if ( distance < minDistance ) { + var vb = d5 * d2 - d1 * d6; + if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { - minDistance = distance; + w = d2 / ( d2 - d6 ); + // edge region of AC; barycentric coords (1-w, 0, w) + return target.copy( a ).addScaledVector( vac, w ); - target.copy( closestPoint ); + } - } + var 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 } - return target; + // face region + var denom = 1 / ( va + vb + vc ); + // u = va * denom + v = vb * denom; + w = vc * denom; + return target.copy( a ).addScaledVector( vab, v ).addScaledVector( vac, w ); }; @@ -14033,6 +14223,120 @@ } ); + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * 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: + * } + */ + + function MeshBasicMaterial( parameters ) { + + Material.call( this ); + + 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.skinning = false; + this.morphTargets = false; + + this.lights = false; + + this.setValues( parameters ); + + } + + MeshBasicMaterial.prototype = Object.create( Material.prototype ); + MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; + + MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + + MeshBasicMaterial.prototype.copy = function ( source ) { + + Material.prototype.copy.call( this, 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; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + + return this; + + }; + /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ @@ -14127,17 +14431,7 @@ if ( morphTargets !== undefined && morphTargets.length > 0 ) { - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) { - - name = morphTargets[ m ].name || String( m ); - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; - - } + console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); } @@ -14159,29 +14453,17 @@ var tempB = new Vector3(); var tempC = new Vector3(); + var morphA = new Vector3(); + var morphB = new Vector3(); + var morphC = new Vector3(); + var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); - var barycoord = new Vector3(); - var intersectionPoint = new Vector3(); var intersectionPointWorld = new Vector3(); - function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { - - Triangle.getBarycoord( point, p1, p2, p3, barycoord ); - - uv1.multiplyScalar( barycoord.x ); - uv2.multiplyScalar( barycoord.y ); - uv3.multiplyScalar( barycoord.z ); - - uv1.add( uv2 ).add( uv3 ); - - return uv1.clone(); - - } - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { var intersect; @@ -14213,13 +14495,44 @@ } - function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) { + function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, uv, a, b, c ) { vA.fromBufferAttribute( position, a ); vB.fromBufferAttribute( position, b ); vC.fromBufferAttribute( position, c ); - var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint ); + var morphInfluences = object.morphTargetInfluences; + + if ( material.morphTargets && morphPosition && morphInfluences ) { + + morphA.set( 0, 0, 0 ); + morphB.set( 0, 0, 0 ); + morphC.set( 0, 0, 0 ); + + for ( var i = 0, il = morphPosition.length; i < il; i ++ ) { + + var influence = morphInfluences[ i ]; + var morphAttribute = morphPosition[ i ]; + + if ( influence === 0 ) continue; + + tempA.fromBufferAttribute( morphAttribute, a ); + tempB.fromBufferAttribute( morphAttribute, b ); + tempC.fromBufferAttribute( morphAttribute, c ); + + morphA.addScaledVector( tempA.sub( vA ), influence ); + morphB.addScaledVector( tempB.sub( vB ), influence ); + morphC.addScaledVector( tempC.sub( vC ), influence ); + + } + + vA.add( morphA ); + vB.add( morphB ); + vC.add( morphC ); + + } + + var intersection = checkIntersection( object, material, raycaster, ray, vA, vB, vC, intersectionPoint ); if ( intersection ) { @@ -14229,7 +14542,7 @@ uvB.fromBufferAttribute( uv, b ); uvC.fromBufferAttribute( uv, c ); - intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); + intersection.uv = Triangle.getUV( intersectionPoint, vA, vB, vC, uvA, uvB, uvC, new Vector2() ); } @@ -14237,7 +14550,6 @@ Triangle.getNormal( vA, vB, vC, face.normal ); intersection.face = face; - intersection.faceIndex = a; } @@ -14282,25 +14594,67 @@ var a, b, c; var index = geometry.index; var position = geometry.attributes.position; + var morphPosition = geometry.morphAttributes.position; var uv = geometry.attributes.uv; - var i, l; + var groups = geometry.groups; + var drawRange = geometry.drawRange; + var i, j, il, jl; + var group, groupMaterial; + var start, end; if ( index !== null ) { // indexed buffer geometry - for ( i = 0, l = index.count; i < l; i += 3 ) { + if ( Array.isArray( material ) ) { + + for ( i = 0, il = groups.length; i < il; i ++ ) { + + group = groups[ i ]; + groupMaterial = material[ group.materialIndex ]; - a = index.getX( i ); - b = index.getX( i + 1 ); - c = index.getX( i + 2 ); + start = Math.max( group.start, drawRange.start ); + end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); - intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); + for ( j = start, jl = end; j < jl; j += 3 ) { - if ( intersection ) { + a = index.getX( j ); + b = index.getX( j + 1 ); + c = index.getX( j + 2 ); - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics - intersects.push( intersection ); + intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, ray, position, morphPosition, uv, a, b, c ); + + if ( intersection ) { + + intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics + intersection.face.materialIndex = group.materialIndex; + intersects.push( intersection ); + + } + + } + + } + + } else { + + start = Math.max( 0, drawRange.start ); + end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + + for ( i = start, il = end; i < il; i += 3 ) { + + a = index.getX( i ); + b = index.getX( i + 1 ); + c = index.getX( i + 2 ); + + intersection = checkBufferGeometryIntersection( this, material, raycaster, ray, position, morphPosition, uv, a, b, c ); + + if ( intersection ) { + + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics + intersects.push( intersection ); + + } } @@ -14310,15 +14664,57 @@ // non-indexed buffer geometry - for ( i = 0, l = position.count; i < l; i += 3 ) { + if ( Array.isArray( material ) ) { + + for ( i = 0, il = groups.length; i < il; i ++ ) { + + group = groups[ i ]; + groupMaterial = material[ group.materialIndex ]; + + start = Math.max( group.start, drawRange.start ); + end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + + for ( j = start, jl = end; j < jl; j += 3 ) { + + a = j; + b = j + 1; + c = j + 2; + + intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, ray, position, morphPosition, uv, a, b, c ); + + if ( intersection ) { + + intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics + intersection.face.materialIndex = group.materialIndex; + intersects.push( intersection ); + + } + + } + + } + + } else { + + start = Math.max( 0, drawRange.start ); + end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); + + for ( i = start, il = end; i < il; i += 3 ) { + + a = i; + b = i + 1; + c = i + 2; + + intersection = checkBufferGeometryIntersection( this, material, raycaster, ray, position, morphPosition, uv, a, b, c ); + + if ( intersection ) { - a = i; - b = i + 1; - c = i + 2; + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics + intersects.push( intersection ); - intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c ); + } - if ( intersection ) intersects.push( intersection ); + } } @@ -14347,39 +14743,6 @@ fvB = vertices[ face.b ]; fvC = vertices[ face.c ]; - if ( faceMaterial.morphTargets === true ) { - - var morphTargets = geometry.morphTargets; - var morphInfluences = this.morphTargetInfluences; - - vA.set( 0, 0, 0 ); - vB.set( 0, 0, 0 ); - vC.set( 0, 0, 0 ); - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var targets = morphTargets[ t ].vertices; - - vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); - vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); - vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); - - } - - vA.add( fvA ); - vB.add( fvB ); - vC.add( fvC ); - - fvA = vA; - fvB = vB; - fvC = vC; - - } - intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); if ( intersection ) { @@ -14391,7 +14754,7 @@ uvB.copy( uvs_f[ 1 ] ); uvC.copy( uvs_f[ 2 ] ); - intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); + intersection.uv = Triangle.getUV( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC, new Vector2() ); } @@ -14421,26 +14784,46 @@ * @author mrdoob / http://mrdoob.com/ */ - function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) { + function WebGLBackground( renderer, state, objects, premultipliedAlpha ) { var clearColor = new Color( 0x000000 ); var clearAlpha = 0; - var planeCamera, planeMesh; + var planeMesh; var boxMesh; + // Store the current background texture and its `version` + // so we can recompile the material accordingly. + var currentBackground = null; + var currentBackgroundVersion = 0; function render( renderList, scene, camera, forceClear ) { var background = scene.background; + // Ignore background in AR + // TODO: Reconsider this. + + var vr = renderer.vr; + var session = vr.getSession && vr.getSession(); + + if ( session && session.environmentBlendMode === 'additive' ) { + + background = null; + + } + if ( background === null ) { setClear( clearColor, clearAlpha ); + currentBackground = null; + currentBackgroundVersion = 0; } else if ( background && background.isColor ) { setClear( background, 1 ); forceClear = true; + currentBackground = null; + currentBackgroundVersion = 0; } @@ -14450,18 +14833,19 @@ } - if ( background && background.isCubeTexture ) { + if ( background && ( background.isCubeTexture || background.isWebGLRenderTargetCube ) ) { if ( boxMesh === undefined ) { boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), new ShaderMaterial( { - uniforms: ShaderLib.cube.uniforms, + type: 'BackgroundCubeMaterial', + uniforms: cloneUniforms( ShaderLib.cube.uniforms ), vertexShader: ShaderLib.cube.vertexShader, fragmentShader: ShaderLib.cube.fragmentShader, side: BackSide, - depthTest: true, + depthTest: false, depthWrite: false, fog: false } ) @@ -14476,34 +14860,96 @@ }; - geometries.update( boxMesh.geometry ); + // enable code injection for non-built-in material + Object.defineProperty( boxMesh.material, 'map', { + + get: function () { + + return this.uniforms.tCube.value; + + } + + } ); + + objects.update( boxMesh ); } - boxMesh.material.uniforms.tCube.value = background; + var texture = background.isWebGLRenderTargetCube ? background.texture : background; + boxMesh.material.uniforms.tCube.value = texture; + boxMesh.material.uniforms.tFlip.value = ( background.isWebGLRenderTargetCube ) ? 1 : - 1; - renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null ); + if ( currentBackground !== background || + currentBackgroundVersion !== texture.version ) { - } else if ( background && background.isTexture ) { + boxMesh.material.needsUpdate = true; - if ( planeCamera === undefined ) { + currentBackground = background; + currentBackgroundVersion = texture.version; - planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + } + + // push to the pre-sorted opaque render list + renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); + + } else if ( background && background.isTexture ) { + + if ( planeMesh === undefined ) { planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), - new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) + new ShaderMaterial( { + type: 'BackgroundMaterial', + uniforms: cloneUniforms( ShaderLib.background.uniforms ), + vertexShader: ShaderLib.background.vertexShader, + fragmentShader: ShaderLib.background.fragmentShader, + side: FrontSide, + depthTest: false, + depthWrite: false, + fog: false + } ) ); - geometries.update( planeMesh.geometry ); + planeMesh.geometry.removeAttribute( 'normal' ); + + // enable code injection for non-built-in material + Object.defineProperty( planeMesh.material, 'map', { + + get: function () { + + return this.uniforms.t2D.value; + + } + + } ); + + objects.update( planeMesh ); + + } + + planeMesh.material.uniforms.t2D.value = background; + + if ( background.matrixAutoUpdate === true ) { + + background.updateMatrix(); } - planeMesh.material.map = background; + planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); + + if ( currentBackground !== background || + currentBackgroundVersion !== background.version ) { + + planeMesh.material.needsUpdate = true; + + currentBackground = background; + currentBackgroundVersion = background.version; + + } - // TODO Push this to renderList - renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null ); + // push to the pre-sorted opaque render list + renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); } @@ -14550,7 +14996,7 @@ * @author mrdoob / http://mrdoob.com/ */ - function WebGLBufferRenderer( gl, extensions, info ) { + function WebGLBufferRenderer( gl, extensions, info, capabilities ) { var mode; @@ -14570,29 +15016,27 @@ function renderInstances( geometry, start, count ) { - var extension = extensions.get( 'ANGLE_instanced_arrays' ); - - if ( extension === null ) { - - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + var extension; - } + if ( capabilities.isWebGL2 ) { - var position = geometry.attributes.position; + extension = gl; - if ( position.isInterleavedBufferAttribute ) { + } else { - count = position.data.count; + extension = extensions.get( 'ANGLE_instanced_arrays' ); - extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); + if ( extension === null ) { - } else { + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount ); + } } + extension[ capabilities.isWebGL2 ? 'drawArraysInstanced' : 'drawArraysInstancedANGLE' ]( mode, start, count, geometry.maxInstancedCount ); + info.update( count, mode, geometry.maxInstancedCount ); } @@ -14637,8 +15081,8 @@ if ( precision === 'highp' ) { - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { + if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && + gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { return 'highp'; @@ -14650,8 +15094,8 @@ if ( precision === 'mediump' ) { - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { + if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && + gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { return 'mediump'; @@ -14663,6 +15107,8 @@ } + var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext; + var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; var maxPrecision = getMaxPrecision( precision ); @@ -14675,22 +15121,26 @@ var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); - var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + var maxTextures = gl.getParameter( 34930 ); + var maxVertexTextures = gl.getParameter( 35660 ); + var maxTextureSize = gl.getParameter( 3379 ); + var maxCubemapSize = gl.getParameter( 34076 ); - var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); - var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); - var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); - var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); + var maxAttributes = gl.getParameter( 34921 ); + var maxVertexUniforms = gl.getParameter( 36347 ); + var maxVaryings = gl.getParameter( 36348 ); + var maxFragmentUniforms = gl.getParameter( 36349 ); var vertexTextures = maxVertexTextures > 0; - var floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + var floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); var floatVertexTextures = vertexTextures && floatFragmentTextures; + var maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; + return { + isWebGL2: isWebGL2, + getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, @@ -14709,7 +15159,9 @@ vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures + floatVertexTextures: floatVertexTextures, + + maxSamples: maxSamples }; @@ -14963,18 +15415,7 @@ delete geometries[ geometry.id ]; - // TODO Remove duplicate code - - var attribute = wireframeAttributes[ geometry.id ]; - - if ( attribute ) { - - attributes.remove( attribute ); - delete wireframeAttributes[ geometry.id ]; - - } - - attribute = wireframeAttributes[ buffergeometry.id ]; + var attribute = wireframeAttributes[ buffergeometry.id ]; if ( attribute ) { @@ -15028,13 +15469,13 @@ if ( index !== null ) { - attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); + attributes.update( index, 34963 ); } for ( var name in geometryAttributes ) { - attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); + attributes.update( geometryAttributes[ name ], 34962 ); } @@ -15048,7 +15489,7 @@ for ( var i = 0, l = array.length; i < l; i ++ ) { - attributes.update( array[ i ], gl.ARRAY_BUFFER ); + attributes.update( array[ i ], 34962 ); } @@ -15103,7 +15544,7 @@ attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER ); + attributes.update( attribute, 34963 ); wireframeAttributes[ geometry.id ] = attribute; @@ -15126,7 +15567,7 @@ * @author mrdoob / http://mrdoob.com/ */ - function WebGLIndexedBufferRenderer( gl, extensions, info ) { + function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { var mode; @@ -15155,16 +15596,26 @@ function renderInstances( geometry, start, count ) { - var extension = extensions.get( 'ANGLE_instanced_arrays' ); + var extension; - if ( extension === null ) { + if ( capabilities.isWebGL2 ) { - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + extension = gl; + + } else { + + var extension = extensions.get( 'ANGLE_instanced_arrays' ); + + if ( extension === null ) { + + console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } } - extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); + extension[ capabilities.isWebGL2 ? 'drawElementsInstanced' : 'drawElementsInstancedANGLE' ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); info.update( count, mode, geometry.maxInstancedCount ); @@ -15206,28 +15657,28 @@ switch ( mode ) { - case gl.TRIANGLES: + case 4: render.triangles += instanceCount * ( count / 3 ); break; - case gl.TRIANGLE_STRIP: - case gl.TRIANGLE_FAN: + case 5: + case 6: render.triangles += instanceCount * ( count - 2 ); break; - case gl.LINES: + case 1: render.lines += instanceCount * ( count / 2 ); break; - case gl.LINE_STRIP: + case 3: render.lines += instanceCount * ( count - 1 ); break; - case gl.LINE_LOOP: + case 2: render.lines += instanceCount * count; break; - case gl.POINTS: + case 0: render.points += instanceCount * count; break; @@ -15427,6 +15878,7 @@ images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; + format = format !== undefined ? format : RGBFormat; Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); @@ -15455,12 +15907,70 @@ } ); + /** + * @author Takahiro https://github.com/takahirox + */ + + function DataTexture2DArray( data, width, height, depth ) { + + Texture.call( this, null ); + + this.image = { data: data, width: width, height: height, depth: depth }; + + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; + + this.wrapR = ClampToEdgeWrapping; + + this.generateMipmaps = false; + this.flipY = false; + + } + + DataTexture2DArray.prototype = Object.create( Texture.prototype ); + DataTexture2DArray.prototype.constructor = DataTexture2DArray; + DataTexture2DArray.prototype.isDataTexture2DArray = true; + + /** + * @author Artur Trzesiok + */ + + function DataTexture3D( data, width, height, depth ) { + + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // var texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 + + Texture.call( this, null ); + + this.image = { data: data, width: width, height: height, depth: depth }; + + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; + + this.wrapR = ClampToEdgeWrapping; + + this.generateMipmaps = false; + this.flipY = false; + + } + + DataTexture3D.prototype = Object.create( Texture.prototype ); + DataTexture3D.prototype.constructor = DataTexture3D; + DataTexture3D.prototype.isDataTexture3D = true; + /** * @author tschw + * @author Mugen87 / https://github.com/Mugen87 + * @author mrdoob / http://mrdoob.com/ * * 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, renderer )'. + * which you get by calling 'new WebGLUniforms( gl, program )'. * * * Properties of inner nodes including the top-level container: @@ -15471,15 +15981,15 @@ * * Methods of all nodes except the top-level container: * - * .setValue( gl, value, [renderer] ) + * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) - * the 'renderer' parameter is needed for sampler uniforms + * the 'textures' parameter is needed for sampler uniforms * * - * Static methods of the top-level container (renderer factorizations): + * Static methods of the top-level container (textures factorizations): * - * .upload( gl, seq, values, renderer ) + * .upload( gl, seq, values, textures ) * * sets uniforms in 'seq' to 'values[id].value' * @@ -15488,16 +15998,12 @@ * filters 'seq' entries with corresponding entry in values * * - * Methods of the top-level container (renderer factorizations): + * Methods of the top-level container (textures factorizations): * - * .setValue( gl, name, value ) + * .setValue( gl, name, value, textures ) * * sets uniform with name 'name' to 'value' * - * .set( gl, obj, prop ) - * - * sets uniform from object and property with same name than uniform - * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object @@ -15505,17 +16011,10 @@ */ var emptyTexture = new Texture(); + var emptyTexture2dArray = new DataTexture2DArray(); + var emptyTexture3d = new DataTexture3D(); var emptyCubeTexture = new CubeTexture(); - // --- Base for inner nodes (including the root) --- - - function UniformContainer() { - - this.seq = []; - this.map = {}; - - } - // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) @@ -15527,6 +16026,7 @@ var mat4array = new Float32Array( 16 ); var mat3array = new Float32Array( 9 ); + var mat2array = new Float32Array( 4 ); // Flattening for arrays of vectors and matrices @@ -15565,9 +16065,33 @@ } + function arraysEqual( a, b ) { + + if ( a.length !== b.length ) return false; + + for ( var i = 0, l = a.length; i < l; i ++ ) { + + if ( a[ i ] !== b[ i ] ) return false; + + } + + return true; + + } + + function copyArray( a, b ) { + + for ( var i = 0, l = b.length; i < l; i ++ ) { + + a[ i ] = b[ i ]; + + } + + } + // Texture unit allocation - function allocTexUnits( renderer, n ) { + function allocTexUnits( textures, n ) { var r = arrayCacheI32[ n ]; @@ -15579,7 +16103,7 @@ } for ( var i = 0; i !== n; ++ i ) - r[ i ] = renderer.allocTextureUnit(); + r[ i ] = textures.allocateTextureUnit(); return r; @@ -15594,27 +16118,52 @@ function setValue1f( gl, v ) { + var cache = this.cache; + + if ( cache[ 0 ] === v ) return; + gl.uniform1f( this.addr, v ); + cache[ 0 ] = v; + } function setValue1i( gl, v ) { + var cache = this.cache; + + if ( cache[ 0 ] === v ) return; + gl.uniform1i( this.addr, v ); + cache[ 0 ] = v; + } // Single float vector (from flat array or THREE.VectorN) function setValue2fv( gl, v ) { - if ( v.x === undefined ) { + var cache = this.cache; - gl.uniform2fv( this.addr, v ); + if ( v.x !== undefined ) { + + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { + + gl.uniform2f( this.addr, v.x, v.y ); + + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + + } } else { - gl.uniform2f( this.addr, v.x, v.y ); + if ( arraysEqual( cache, v ) ) return; + + gl.uniform2fv( this.addr, v ); + + copyArray( cache, v ); } @@ -15622,31 +16171,68 @@ function setValue3fv( gl, v ) { + var cache = this.cache; + if ( v.x !== undefined ) { - gl.uniform3f( this.addr, v.x, v.y, v.z ); + 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 ) { - gl.uniform3f( this.addr, v.r, v.g, v.b ); + if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { + + gl.uniform3f( this.addr, v.r, v.g, v.b ); + + cache[ 0 ] = v.r; + cache[ 1 ] = v.g; + cache[ 2 ] = v.b; + + } } else { + if ( arraysEqual( cache, v ) ) return; + gl.uniform3fv( this.addr, v ); + copyArray( cache, v ); + } } function setValue4fv( gl, v ) { - if ( v.x === undefined ) { + var cache = this.cache; - gl.uniform4fv( this.addr, v ); + if ( v.x !== undefined ) { + + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { + + gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; + + } } else { - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + if ( arraysEqual( cache, v ) ) return; + + gl.uniform4fv( this.addr, v ); + + copyArray( cache, v ); } @@ -15656,55 +16242,148 @@ function setValue2fm( gl, v ) { - gl.uniformMatrix2fv( this.addr, false, v.elements || v ); + var cache = this.cache; + var elements = v.elements; + + if ( elements === undefined ) { + + if ( arraysEqual( cache, v ) ) return; + + gl.uniformMatrix2fv( this.addr, false, v ); + + copyArray( cache, v ); + + } else { + + if ( arraysEqual( cache, elements ) ) return; + + mat2array.set( elements ); + + gl.uniformMatrix2fv( this.addr, false, mat2array ); + + copyArray( cache, elements ); + + } } function setValue3fm( gl, v ) { - if ( v.elements === undefined ) { + var cache = this.cache; + var elements = v.elements; + + if ( elements === undefined ) { + + if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix3fv( this.addr, false, v ); + copyArray( cache, v ); + } else { - mat3array.set( v.elements ); + if ( arraysEqual( cache, elements ) ) return; + + mat3array.set( elements ); + gl.uniformMatrix3fv( this.addr, false, mat3array ); + copyArray( cache, elements ); + } } function setValue4fm( gl, v ) { - if ( v.elements === undefined ) { + var cache = this.cache; + var elements = v.elements; + + if ( elements === undefined ) { + + if ( arraysEqual( cache, v ) ) return; gl.uniformMatrix4fv( this.addr, false, v ); + copyArray( cache, v ); + } else { - mat4array.set( v.elements ); + if ( arraysEqual( cache, elements ) ) return; + + mat4array.set( elements ); + gl.uniformMatrix4fv( this.addr, false, mat4array ); + copyArray( cache, elements ); + } } // Single texture (2D / Cube) - function setValueT1( gl, v, renderer ) { + function setValueT1( gl, v, textures ) { + + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; + + } + + textures.safeSetTexture2D( v || emptyTexture, unit ); + + } + + function setValueT2DArray1( gl, v, textures ) { + + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; + + } + + textures.setTexture2DArray( v || emptyTexture2dArray, unit ); + + } + + function setValueT3D1( gl, v, textures ) { + + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; + + } - var unit = renderer.allocTextureUnit(); - gl.uniform1i( this.addr, unit ); - renderer.setTexture2D( v || emptyTexture, unit ); + textures.setTexture3D( v || emptyTexture3d, unit ); } - function setValueT6( gl, v, renderer ) { + function setValueT6( gl, v, textures ) { + + var cache = this.cache; + var unit = textures.allocateTextureUnit(); + + if ( cache[ 0 ] !== unit ) { + + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; + + } - var unit = renderer.allocTextureUnit(); - gl.uniform1i( this.addr, unit ); - renderer.setTextureCube( v || emptyCubeTexture, unit ); + textures.safeSetTextureCube( v || emptyCubeTexture, unit ); } @@ -15712,20 +16391,38 @@ function setValue2iv( gl, v ) { + var cache = this.cache; + + if ( arraysEqual( cache, v ) ) return; + gl.uniform2iv( this.addr, v ); + copyArray( cache, v ); + } function setValue3iv( gl, v ) { + var cache = this.cache; + + if ( arraysEqual( cache, v ) ) return; + gl.uniform3iv( this.addr, v ); + copyArray( cache, v ); + } function setValue4iv( gl, v ) { + var cache = this.cache; + + if ( arraysEqual( cache, v ) ) return; + gl.uniform4iv( this.addr, v ); + copyArray( cache, v ); + } // Helper to pick the right setter for the singular case @@ -15744,7 +16441,9 @@ case 0x8b5c: return setValue4fm; // _MAT4 case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES + case 0x8b5f: return setValueT3D1; // SAMPLER_3D case 0x8b60: return setValueT6; // SAMPLER_CUBE + case 0x8DC1: return setValueT2DArray1; // SAMPLER_2D_ARRAY case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 @@ -15759,32 +16458,65 @@ function setValue1fv( gl, v ) { + var cache = this.cache; + + if ( arraysEqual( cache, v ) ) return; + gl.uniform1fv( this.addr, v ); + copyArray( cache, v ); + } function setValue1iv( gl, v ) { + var cache = this.cache; + + if ( arraysEqual( cache, v ) ) return; + gl.uniform1iv( this.addr, v ); + copyArray( cache, v ); + } // Array of vectors (flat or from THREE classes) function setValueV2a( gl, v ) { - gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 2 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniform2fv( this.addr, data ); + + this.updateCache( data ); } function setValueV3a( gl, v ) { - gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 3 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniform3fv( this.addr, data ); + + this.updateCache( data ); } function setValueV4a( gl, v ) { - gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 4 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniform4fv( this.addr, data ); + + this.updateCache( data ); } @@ -15792,49 +16524,84 @@ function setValueM2a( gl, v ) { - gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 4 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniformMatrix2fv( this.addr, false, data ); + + this.updateCache( data ); } function setValueM3a( gl, v ) { - gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 9 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniformMatrix3fv( this.addr, false, data ); + + this.updateCache( data ); } function setValueM4a( gl, v ) { - gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); + var cache = this.cache; + var data = flatten( v, this.size, 16 ); + + if ( arraysEqual( cache, data ) ) return; + + gl.uniformMatrix4fv( this.addr, false, data ); + + this.updateCache( data ); } // Array of textures (2D / Cube) - function setValueT1a( gl, v, renderer ) { + function setValueT1a( gl, v, textures ) { + + var cache = this.cache; + var n = v.length; - var n = v.length, - units = allocTexUnits( renderer, n ); + var units = allocTexUnits( textures, n ); - gl.uniform1iv( this.addr, units ); + if ( arraysEqual( cache, units ) === false ) { + + gl.uniform1iv( this.addr, units ); + copyArray( cache, units ); + + } for ( var i = 0; i !== n; ++ i ) { - renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); + textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } - function setValueT6a( gl, v, renderer ) { + function setValueT6a( gl, v, textures ) { - var n = v.length, - units = allocTexUnits( renderer, n ); + var cache = this.cache; + var n = v.length; - gl.uniform1iv( this.addr, units ); + var units = allocTexUnits( textures, n ); + + if ( arraysEqual( cache, units ) === false ) { + + gl.uniform1iv( this.addr, units ); + copyArray( cache, units ); + + } for ( var i = 0; i !== n; ++ i ) { - renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); + textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } @@ -15873,6 +16640,7 @@ this.id = id; this.addr = addr; + this.cache = []; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG @@ -15883,6 +16651,7 @@ this.id = id; this.addr = addr; + this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); @@ -15890,25 +16659,37 @@ } + PureArrayUniform.prototype.updateCache = function ( data ) { + + var cache = this.cache; + + if ( data instanceof Float32Array && cache.length !== data.length ) { + + this.cache = new Float32Array( data.length ); + + } + + copyArray( cache, data ); + + }; + function StructuredUniform( id ) { this.id = id; - UniformContainer.call( this ); // mix-in + this.seq = []; + this.map = {}; } - StructuredUniform.prototype.setValue = function ( gl, value ) { - - // Note: Don't need an extra 'renderer' parameter, since samplers - // are not allowed in structured uniforms. + StructuredUniform.prototype.setValue = function ( gl, value, textures ) { var seq = this.seq; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; - u.setValue( gl, value[ u.id ] ); + u.setValue( gl, value[ u.id ], textures ); } @@ -15944,7 +16725,7 @@ // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; - for ( ; ; ) { + while ( true ) { var match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex, @@ -15988,13 +16769,12 @@ // Root Container - function WebGLUniforms( gl, program, renderer ) { + function WebGLUniforms( gl, program ) { - UniformContainer.call( this ); - - this.renderer = renderer; + this.seq = []; + this.map = {}; - var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); + var n = gl.getProgramParameter( program, 35718 ); for ( var i = 0; i < n; ++ i ) { @@ -16007,11 +16787,11 @@ } - WebGLUniforms.prototype.setValue = function ( gl, name, value ) { + WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { var u = this.map[ name ]; - if ( u !== undefined ) u.setValue( gl, value, this.renderer ); + if ( u !== undefined ) u.setValue( gl, value, textures ); }; @@ -16026,7 +16806,7 @@ // Static interface - WebGLUniforms.upload = function ( gl, seq, values, renderer ) { + WebGLUniforms.upload = function ( gl, seq, values, textures ) { for ( var i = 0, n = seq.length; i !== n; ++ i ) { @@ -16036,7 +16816,7 @@ if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, renderer ); + u.setValue( gl, v.value, textures ); } @@ -16077,22 +16857,26 @@ } - function WebGLShader( gl, type, string ) { + function WebGLShader( gl, type, string, debug ) { var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); - if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + if ( debug === true ) { - console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + if ( gl.getShaderParameter( shader, 35713 ) === false ) { - } + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + + } - if ( gl.getShaderInfoLog( shader ) !== '' ) { + if ( gl.getShaderInfoLog( shader ) !== '' ) { - console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === 35633 ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + + } } @@ -16170,6 +16954,10 @@ toneMappingName = 'OptimizedCineon'; break; + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; + default: throw new Error( 'unsupported toneMapping: ' + toneMapping ); @@ -16184,7 +16972,7 @@ extensions = extensions || {}; var chunks = [ - ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '' @@ -16216,7 +17004,7 @@ var attributes = {}; - var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); + var n = gl.getProgramParameter( program, 35721 ); for ( var i = 0; i < n; i ++ ) { @@ -16260,7 +17048,7 @@ function parseIncludes( string ) { - var pattern = /^[ \t]*#include +<([\w\d.]+)>/gm; + var pattern = /^[ \t]*#include +<([\w\d./]+)>/gm; function replace( match, include ) { @@ -16302,7 +17090,7 @@ } - function WebGLProgram( renderer, extensions, code, material, shader, parameters ) { + function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures ) { var gl = renderer.context; @@ -16385,7 +17173,7 @@ // - var customExtensions = generateExtensions( material.extensions, parameters, extensions ); + var customExtensions = capabilities.isWebGL2 ? '' : generateExtensions( material.extensions, parameters, extensions ); var customDefines = generateDefines( defines ); @@ -16449,11 +17237,14 @@ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_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.vertexTangents ? '#define USE_TANGENT' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', @@ -16472,7 +17263,7 @@ parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', @@ -16485,6 +17276,12 @@ 'attribute vec3 normal;', 'attribute vec2 uv;', + '#ifdef USE_TANGENT', + + ' attribute vec4 tangent;', + + '#endif', + '#ifdef USE_COLOR', ' attribute vec3 color;', @@ -16538,7 +17335,7 @@ customDefines, - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer '#define GAMMA_FACTOR ' + gammaFactorDefine, @@ -16546,6 +17343,7 @@ ( parameters.useFog && parameters.fogExp ) ? '#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 : '', @@ -16555,10 +17353,13 @@ parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', + ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + + parameters.vertexTangents ? '#define USE_TANGENT' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', @@ -16576,9 +17377,9 @@ parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - parameters.envMap && extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', + parameters.envMap && ( capabilities.isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) ) ? '#define TEXTURE_LOD_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', @@ -16589,8 +17390,10 @@ parameters.dithering ? '#define DITHERING' : '', - ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below + ( parameters.outputEncoding || parameters.mapEncoding || parameters.matcapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? + ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', + parameters.matcapEncoding ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '', @@ -16614,14 +17417,58 @@ vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); + if ( capabilities.isWebGL2 && ! material.isRawShaderMaterial ) { + + var isGLSL3ShaderMaterial = false; + + var versionRegex = /^\s*#version\s+300\s+es\s*\n/; + + if ( material.isShaderMaterial && + vertexShader.match( versionRegex ) !== null && + fragmentShader.match( versionRegex ) !== null ) { + + isGLSL3ShaderMaterial = true; + + vertexShader = vertexShader.replace( versionRegex, '' ); + fragmentShader = fragmentShader.replace( versionRegex, '' ); + + } + + // GLSL 3.0 conversion + prefixVertex = [ + '#version 300 es\n', + '#define attribute in', + '#define varying out', + '#define texture2D texture' + ].join( '\n' ) + '\n' + prefixVertex; + + prefixFragment = [ + '#version 300 es\n', + '#define varying in', + isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;', + isGLSL3ShaderMaterial ? '' : '#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; + + } + var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); - var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); - var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); + var glVertexShader = WebGLShader( gl, 35633, vertexGlsl, renderer.debug.checkShaderErrors ); + var glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl, renderer.debug.checkShaderErrors ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); @@ -16641,56 +17488,61 @@ gl.linkProgram( program ); - var programLog = gl.getProgramInfoLog( program ).trim(); - var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); + // check for link errors + if ( renderer.debug.checkShaderErrors ) { - var runnable = true; - var haveDiagnostics = true; + var programLog = gl.getProgramInfoLog( program ).trim(); + var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); - // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); + var runnable = true; + var haveDiagnostics = true; - if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { + // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); + // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); - runnable = false; + if ( gl.getProgramParameter( program, 35714 ) === false ) { - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); + runnable = false; - } else if ( programLog !== '' ) { + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + } else if ( programLog !== '' ) { - } else if ( vertexLog === '' || fragmentLog === '' ) { + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - haveDiagnostics = false; + } else if ( vertexLog === '' || fragmentLog === '' ) { - } + haveDiagnostics = false; - if ( haveDiagnostics ) { + } - this.diagnostics = { + if ( haveDiagnostics ) { - runnable: runnable, - material: material, + this.diagnostics = { - programLog: programLog, + runnable: runnable, + material: material, - vertexShader: { + programLog: programLog, - log: vertexLog, - prefix: prefixVertex + vertexShader: { - }, + log: vertexLog, + prefix: prefixVertex - fragmentShader: { + }, - log: fragmentLog, - prefix: prefixFragment + fragmentShader: { - } + log: fragmentLog, + prefix: prefixFragment - }; + } + + }; + + } } @@ -16707,7 +17559,7 @@ if ( cachedUniforms === undefined ) { - cachedUniforms = new WebGLUniforms( gl, program, renderer ); + cachedUniforms = new WebGLUniforms( gl, program, textures ); } @@ -16783,7 +17635,7 @@ * @author mrdoob / http://mrdoob.com/ */ - function WebGLPrograms( renderer, extensions, capabilities ) { + function WebGLPrograms( renderer, extensions, capabilities, textures ) { var programs = []; @@ -16797,17 +17649,19 @@ MeshToonMaterial: 'phong', MeshStandardMaterial: 'physical', MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', LineBasicMaterial: 'basic', LineDashedMaterial: 'dashed', PointsMaterial: 'points', - ShadowMaterial: 'shadow' + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' }; var parameterNames = [ - "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", - "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", + "precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", + "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", + "alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", @@ -16916,6 +17770,8 @@ outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), + matcap: !! material.matcap, + matcapEncoding: getTextureEncodingFromMap( material.matcap, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), @@ -16926,6 +17782,7 @@ emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, + objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, @@ -16936,6 +17793,7 @@ combine: material.combine, + vertexTangents: ( material.normalMap && material.vertexTangents ), vertexColors: material.vertexColors, fog: !! fog, @@ -17023,6 +17881,8 @@ array.push( renderer.gammaOutput ); + array.push( renderer.gammaFactor ); + return array.join(); }; @@ -17049,7 +17909,7 @@ if ( program === undefined ) { - program = new WebGLProgram( renderer, extensions, code, material, shader, parameters ); + program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures ); programs.push( program ); } @@ -17135,11 +17995,15 @@ function painterSortStable( a, b ) { - if ( a.renderOrder !== b.renderOrder ) { + if ( a.groupOrder !== b.groupOrder ) { + + return a.groupOrder - b.groupOrder; + + } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; - } else if ( a.program && b.program && a.program !== b.program ) { + } else if ( a.program !== b.program ) { return a.program.id - b.program.id; @@ -17161,11 +18025,15 @@ function reversePainterSortStable( a, b ) { - if ( a.renderOrder !== b.renderOrder ) { + if ( a.groupOrder !== b.groupOrder ) { + + return a.groupOrder - b.groupOrder; + + } else if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; - } if ( a.z !== b.z ) { + } else if ( a.z !== b.z ) { return b.z - a.z; @@ -17177,6 +18045,7 @@ } + function WebGLRenderList() { var renderItems = []; @@ -17185,6 +18054,8 @@ var opaque = []; var transparent = []; + var defaultProgram = { id: - 1 }; + function init() { renderItemsIndex = 0; @@ -17194,7 +18065,7 @@ } - function push( object, geometry, material, z, group ) { + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { var renderItem = renderItems[ renderItemsIndex ]; @@ -17205,7 +18076,8 @@ object: object, geometry: geometry, material: material, - program: material.program, + program: material.program || defaultProgram, + groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group @@ -17219,16 +18091,33 @@ renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; - renderItem.program = material.program; + renderItem.program = material.program || defaultProgram; + renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } + renderItemsIndex ++; + + return renderItem; + + } + + function push( object, geometry, material, groupOrder, z, group ) { + + var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + ( material.transparent === true ? transparent : opaque ).push( renderItem ); - renderItemsIndex ++; + } + + function unshift( object, geometry, material, groupOrder, z, group ) { + + var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); + + ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); } @@ -17245,6 +18134,7 @@ init: init, push: push, + unshift: unshift, sort: sort }; @@ -17255,17 +18145,37 @@ var lists = {}; - function get( scene, camera ) { + function onSceneDispose( event ) { + + var scene = event.target; + + scene.removeEventListener( 'dispose', onSceneDispose ); - var hash = scene.id + ',' + camera.id; - var list = lists[ hash ]; + delete lists[ scene.id ]; - if ( list === undefined ) { + } + + function get( scene, camera ) { - // console.log( 'THREE.WebGLRenderLists:', hash ); + var cameras = lists[ scene.id ]; + var list; + if ( cameras === undefined ) { list = new WebGLRenderList(); - lists[ hash ] = list; + lists[ scene.id ] = {}; + lists[ scene.id ][ camera.id ] = list; + + scene.addEventListener( 'dispose', onSceneDispose ); + + } else { + + list = cameras[ camera.id ]; + if ( list === undefined ) { + + list = new WebGLRenderList(); + cameras[ camera.id ] = list; + + } } @@ -17393,9 +18303,18 @@ id: count ++, - hash: '', + hash: { + stateID: - 1, + directionalLength: - 1, + pointLength: - 1, + spotLength: - 1, + rectAreaLength: - 1, + hemiLength: - 1, + shadowsLength: - 1 + }, ambient: [ 0, 0, 0 ], + probe: [], directional: [], directionalShadowMap: [], directionalShadowMatrix: [], @@ -17410,6 +18329,8 @@ }; + for ( var i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); + var vector3 = new Vector3(); var matrix4 = new Matrix4(); var matrix42 = new Matrix4(); @@ -17418,6 +18339,8 @@ var r = 0, g = 0, b = 0; + for ( var i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); + var directionalLength = 0; var pointLength = 0; var spotLength = 0; @@ -17442,6 +18365,14 @@ g += color.g * intensity; b += color.b * intensity; + } else if ( light.isLightProbe ) { + + for ( var j = 0; j < 9; j ++ ) { + + state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); + + } + } else if ( light.isDirectionalLight ) { var uniforms = cache.get( light ); @@ -17487,7 +18418,7 @@ uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; + uniforms.decay = light.decay; uniforms.shadow = light.castShadow; @@ -17548,7 +18479,7 @@ uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.distance = light.distance; - uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; + uniforms.decay = light.decay; uniforms.shadow = light.castShadow; @@ -17599,7 +18530,13 @@ state.point.length = pointLength; state.hemi.length = hemiLength; - state.hash = state.id + ',' + directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length; + state.hash.stateID = state.id; + state.hash.directionalLength = directionalLength; + state.hash.pointLength = pointLength; + state.hash.spotLength = spotLength; + state.hash.rectAreaLength = rectAreaLength; + state.hash.hemiLength = hemiLength; + state.hash.shadowsLength = shadows.length; } @@ -17620,13 +18557,11 @@ var lightsArray = []; var shadowsArray = []; - var spritesArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; - spritesArray.length = 0; } @@ -17642,12 +18577,6 @@ } - function pushSprite( shadowLight ) { - - spritesArray.push( shadowLight ); - - } - function setupLights( camera ) { lights.setup( lightsArray, shadowsArray, camera ); @@ -17657,7 +18586,6 @@ var state = { lightsArray: lightsArray, shadowsArray: shadowsArray, - spritesArray: spritesArray, lights: lights }; @@ -17668,8 +18596,7 @@ setupLights: setupLights, pushLight: pushLight, - pushShadow: pushShadow, - pushSprite: pushSprite + pushShadow: pushShadow }; } @@ -17678,16 +18605,40 @@ var renderStates = {}; - function get( scene, camera ) { + function onSceneDispose( event ) { + + var scene = event.target; + + scene.removeEventListener( 'dispose', onSceneDispose ); - var hash = scene.id + ',' + camera.id; + delete renderStates[ scene.id ]; - var renderState = renderStates[ hash ]; + } + + function get( scene, camera ) { - if ( renderState === undefined ) { + var renderState; + + if ( renderStates[ scene.id ] === undefined ) { renderState = new WebGLRenderState(); - renderStates[ hash ] = renderState; + renderStates[ scene.id ] = {}; + renderStates[ scene.id ][ camera.id ] = renderState; + + scene.addEventListener( 'dispose', onSceneDispose ); + + } else { + + if ( renderStates[ scene.id ][ camera.id ] === undefined ) { + + renderState = new WebGLRenderState(); + renderStates[ scene.id ][ camera.id ] = renderState; + + } else { + + renderState = renderStates[ scene.id ][ camera.id ]; + + } } @@ -17960,12 +18911,12 @@ if ( lights.length === 0 ) return; - // TODO Clean up (needed in case of contextlost) - var _gl = _renderer.context; + var currentRenderTarget = _renderer.getRenderTarget(); + var _state = _renderer.state; // Set GL state for depth map. - _state.disable( _gl.BLEND ); + _state.setBlending( NoBlending ); _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.buffers.depth.setTest( true ); _state.setScissorTest( false ); @@ -18118,6 +19069,8 @@ scope.needsUpdate = false; + _renderer.setRenderTarget( currentRenderTarget ); + }; function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) { @@ -18290,403 +19243,7 @@ * @author mrdoob / http://mrdoob.com/ */ - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.needsUpdate = true; - - } - - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ - - function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) { - - var vertexBuffer, elementBuffer; - var program, attributes, uniforms; - - var texture; - - // decompose matrixWorld - - var spritePosition = new Vector3(); - var spriteRotation = new Quaternion(); - var spriteScale = new Vector3(); - - function init() { - - var vertices = new Float32Array( [ - - 0.5, - 0.5, 0, 0, - 0.5, - 0.5, 1, 0, - 0.5, 0.5, 1, 1, - - 0.5, 0.5, 0, 1 - ] ); - - var faces = new Uint16Array( [ - 0, 1, 2, - 0, 2, 3 - ] ); - - vertexBuffer = gl.createBuffer(); - elementBuffer = gl.createBuffer(); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - - program = createProgram(); - - attributes = { - position: gl.getAttribLocation( program, 'position' ), - uv: gl.getAttribLocation( program, 'uv' ) - }; - - uniforms = { - uvOffset: gl.getUniformLocation( program, 'uvOffset' ), - uvScale: gl.getUniformLocation( program, 'uvScale' ), - - rotation: gl.getUniformLocation( program, 'rotation' ), - center: gl.getUniformLocation( program, 'center' ), - scale: gl.getUniformLocation( program, 'scale' ), - - color: gl.getUniformLocation( program, 'color' ), - map: gl.getUniformLocation( program, 'map' ), - opacity: gl.getUniformLocation( program, 'opacity' ), - - modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), - projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), - - fogType: gl.getUniformLocation( program, 'fogType' ), - fogDensity: gl.getUniformLocation( program, 'fogDensity' ), - fogNear: gl.getUniformLocation( program, 'fogNear' ), - fogFar: gl.getUniformLocation( program, 'fogFar' ), - fogColor: gl.getUniformLocation( program, 'fogColor' ), - fogDepth: gl.getUniformLocation( program, 'fogDepth' ), - - alphaTest: gl.getUniformLocation( program, 'alphaTest' ) - }; - - var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = 8; - canvas.height = 8; - - var context = canvas.getContext( '2d' ); - context.fillStyle = 'white'; - context.fillRect( 0, 0, 8, 8 ); - - texture = new CanvasTexture( canvas ); - - } - - this.render = function ( sprites, scene, camera ) { - - if ( sprites.length === 0 ) return; - - // setup gl - - if ( program === undefined ) { - - init(); - - } - - state.useProgram( program ); - - state.initAttributes(); - state.enableAttribute( attributes.position ); - state.enableAttribute( attributes.uv ); - state.disableUnusedAttributes(); - - state.disable( gl.CULL_FACE ); - state.enable( gl.BLEND ); - - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); - gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); - - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - - gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - - state.activeTexture( gl.TEXTURE0 ); - gl.uniform1i( uniforms.map, 0 ); - - var oldFogType = 0; - var sceneFogType = 0; - var fog = scene.fog; - - if ( fog ) { - - gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); - - if ( fog.isFog ) { - - gl.uniform1f( uniforms.fogNear, fog.near ); - gl.uniform1f( uniforms.fogFar, fog.far ); - - gl.uniform1i( uniforms.fogType, 1 ); - oldFogType = 1; - sceneFogType = 1; - - } else if ( fog.isFogExp2 ) { - - gl.uniform1f( uniforms.fogDensity, fog.density ); - - gl.uniform1i( uniforms.fogType, 2 ); - oldFogType = 2; - sceneFogType = 2; - - } - - } else { - - gl.uniform1i( uniforms.fogType, 0 ); - oldFogType = 0; - sceneFogType = 0; - - } - - - // update positions and sort - - for ( var i = 0, l = sprites.length; i < l; i ++ ) { - - var sprite = sprites[ i ]; - - sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; - - } - - sprites.sort( painterSortStable ); - - // render all sprites - - var scale = []; - var center = []; - - for ( var i = 0, l = sprites.length; i < l; i ++ ) { - - var sprite = sprites[ i ]; - var material = sprite.material; - - if ( material.visible === false ) continue; - - sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined ); - - gl.uniform1f( uniforms.alphaTest, material.alphaTest ); - gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); - - sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); - - scale[ 0 ] = spriteScale.x; - scale[ 1 ] = spriteScale.y; - - center[ 0 ] = sprite.center.x - 0.5; - center[ 1 ] = sprite.center.y - 0.5; - - var fogType = 0; - - if ( scene.fog && material.fog ) { - - fogType = sceneFogType; - - } - - if ( oldFogType !== fogType ) { - - gl.uniform1i( uniforms.fogType, fogType ); - oldFogType = fogType; - - } - - if ( material.map !== null ) { - - gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); - gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); - - } else { - - gl.uniform2f( uniforms.uvOffset, 0, 0 ); - gl.uniform2f( uniforms.uvScale, 1, 1 ); - - } - - gl.uniform1f( uniforms.opacity, material.opacity ); - gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); - - gl.uniform1f( uniforms.rotation, material.rotation ); - gl.uniform2fv( uniforms.center, center ); - gl.uniform2fv( uniforms.scale, scale ); - - state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - state.buffers.depth.setTest( material.depthTest ); - state.buffers.depth.setMask( material.depthWrite ); - state.buffers.color.setMask( material.colorWrite ); - - textures.setTexture2D( material.map || texture, 0 ); - - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined ); - - } - - // restore gl - - state.enable( gl.CULL_FACE ); - - state.reset(); - - }; - - function createProgram() { - - var program = gl.createProgram(); - - var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); - - gl.shaderSource( vertexShader, [ - - 'precision ' + capabilities.precision + ' float;', - - '#define SHADER_NAME ' + 'SpriteMaterial', - - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform float rotation;', - 'uniform vec2 center;', - 'uniform vec2 scale;', - 'uniform vec2 uvOffset;', - 'uniform vec2 uvScale;', - - 'attribute vec2 position;', - 'attribute vec2 uv;', - - 'varying vec2 vUV;', - 'varying float fogDepth;', - - 'void main() {', - - ' vUV = uvOffset + uv * uvScale;', - - ' vec2 alignedPosition = ( position - center ) * scale;', - - ' vec2 rotatedPosition;', - ' rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', - ' rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', - - ' vec4 mvPosition;', - - ' mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', - ' mvPosition.xy += rotatedPosition;', - - ' gl_Position = projectionMatrix * mvPosition;', - - ' fogDepth = - mvPosition.z;', - - '}' - - ].join( '\n' ) ); - - gl.shaderSource( fragmentShader, [ - - 'precision ' + capabilities.precision + ' float;', - - '#define SHADER_NAME ' + 'SpriteMaterial', - - 'uniform vec3 color;', - 'uniform sampler2D map;', - 'uniform float opacity;', - - 'uniform int fogType;', - 'uniform vec3 fogColor;', - 'uniform float fogDensity;', - 'uniform float fogNear;', - 'uniform float fogFar;', - 'uniform float alphaTest;', - - 'varying vec2 vUV;', - 'varying float fogDepth;', - - 'void main() {', - - ' vec4 texture = texture2D( map, vUV );', - - ' gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', - - ' if ( gl_FragColor.a < alphaTest ) discard;', - - ' if ( fogType > 0 ) {', - - ' float fogFactor = 0.0;', - - ' if ( fogType == 1 ) {', - - ' fogFactor = smoothstep( fogNear, fogFar, fogDepth );', - - ' } else {', - - ' const float LOG2 = 1.442695;', - ' fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );', - ' fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', - - ' }', - - ' gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );', - - ' }', - - '}' - - ].join( '\n' ) ); - - gl.compileShader( vertexShader ); - gl.compileShader( fragmentShader ); - - gl.attachShader( program, vertexShader ); - gl.attachShader( program, fragmentShader ); - - gl.linkProgram( program ); - - return program; - - } - - function painterSortStable( a, b ) { - - if ( a.renderOrder !== b.renderOrder ) { - - return a.renderOrder - b.renderOrder; - - } else if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { - - return b.id - a.id; - - } - - } - - } - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function WebGLState( gl, extensions, utils ) { + function WebGLState( gl, extensions, utils, capabilities ) { function ColorBuffer() { @@ -18761,11 +19318,11 @@ if ( depthTest ) { - enable( gl.DEPTH_TEST ); + enable( 2929 ); } else { - disable( gl.DEPTH_TEST ); + disable( 2929 ); } @@ -18792,53 +19349,53 @@ case NeverDepth: - gl.depthFunc( gl.NEVER ); + gl.depthFunc( 512 ); break; case AlwaysDepth: - gl.depthFunc( gl.ALWAYS ); + gl.depthFunc( 519 ); break; case LessDepth: - gl.depthFunc( gl.LESS ); + gl.depthFunc( 513 ); break; case LessEqualDepth: - gl.depthFunc( gl.LEQUAL ); + gl.depthFunc( 515 ); break; case EqualDepth: - gl.depthFunc( gl.EQUAL ); + gl.depthFunc( 514 ); break; case GreaterEqualDepth: - gl.depthFunc( gl.GEQUAL ); + gl.depthFunc( 518 ); break; case GreaterDepth: - gl.depthFunc( gl.GREATER ); + gl.depthFunc( 516 ); break; case NotEqualDepth: - gl.depthFunc( gl.NOTEQUAL ); + gl.depthFunc( 517 ); break; default: - gl.depthFunc( gl.LEQUAL ); + gl.depthFunc( 515 ); } } else { - gl.depthFunc( gl.LEQUAL ); + gl.depthFunc( 515 ); } @@ -18898,11 +19455,11 @@ if ( stencilTest ) { - enable( gl.STENCIL_TEST ); + enable( 2960 ); } else { - disable( gl.STENCIL_TEST ); + disable( 2960 ); } @@ -18993,17 +19550,18 @@ var depthBuffer = new DepthBuffer(); var stencilBuffer = new StencilBuffer(); - var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + var maxVertexAttributes = gl.getParameter( 34921 ); var newAttributes = new Uint8Array( maxVertexAttributes ); var enabledAttributes = new Uint8Array( maxVertexAttributes ); var attributeDivisors = new Uint8Array( maxVertexAttributes ); - var capabilities = {}; + var enabledCapabilities = {}; var compressedTextureFormats = null; var currentProgram = null; + var currentBlendingEnabled = null; var currentBlending = null; var currentBlendEquation = null; var currentBlendSrc = null; @@ -19021,11 +19579,11 @@ var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; - var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); + var maxTextures = gl.getParameter( 35661 ); var lineWidthAvailable = false; var version = 0; - var glVersion = gl.getParameter( gl.VERSION ); + var glVersion = gl.getParameter( 7938 ); if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { @@ -19051,12 +19609,12 @@ var texture = gl.createTexture(); gl.bindTexture( type, texture ); - gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( type, 10241, 9728 ); + gl.texParameteri( type, 10240, 9728 ); for ( var i = 0; i < count; i ++ ) { - gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); + gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); } @@ -19065,8 +19623,8 @@ } var emptyTextures = {}; - emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); - emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); + emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); + emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); // init @@ -19074,15 +19632,14 @@ depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); - enable( gl.DEPTH_TEST ); + enable( 2929 ); depthBuffer.setFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); - enable( gl.CULL_FACE ); + enable( 2884 ); - enable( gl.BLEND ); - setBlending( NormalBlending ); + setBlending( NoBlending ); // @@ -19098,23 +19655,7 @@ function enableAttribute( attribute ) { - newAttributes[ attribute ] = 1; - - if ( enabledAttributes[ attribute ] === 0 ) { - - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; - - } - - if ( attributeDivisors[ attribute ] !== 0 ) { - - var extension = extensions.get( 'ANGLE_instanced_arrays' ); - - extension.vertexAttribDivisorANGLE( attribute, 0 ); - attributeDivisors[ attribute ] = 0; - - } + enableAttributeAndDivisor( attribute, 0 ); } @@ -19131,9 +19672,9 @@ if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - var extension = extensions.get( 'ANGLE_instanced_arrays' ); + var extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); + extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } @@ -19157,10 +19698,10 @@ function enable( id ) { - if ( capabilities[ id ] !== true ) { + if ( enabledCapabilities[ id ] !== true ) { gl.enable( id ); - capabilities[ id ] = true; + enabledCapabilities[ id ] = true; } @@ -19168,10 +19709,10 @@ function disable( id ) { - if ( capabilities[ id ] !== false ) { + if ( enabledCapabilities[ id ] !== false ) { gl.disable( id ); - capabilities[ id ] = false; + enabledCapabilities[ id ] = false; } @@ -19188,7 +19729,7 @@ extensions.get( 'WEBGL_compressed_texture_etc1' ) || extensions.get( 'WEBGL_compressed_texture_astc' ) ) { - var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); + var formats = gl.getParameter( 34467 ); for ( var i = 0; i < formats.length; i ++ ) { @@ -19222,139 +19763,152 @@ function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - if ( blending !== NoBlending ) { + if ( blending === NoBlending ) { - enable( gl.BLEND ); + if ( currentBlendingEnabled ) { - } else { + disable( 3042 ); + currentBlendingEnabled = false; + + } - disable( gl.BLEND ); + return; } - if ( blending !== CustomBlending ) { + if ( ! currentBlendingEnabled ) { - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + enable( 3042 ); + currentBlendingEnabled = true; - switch ( blending ) { + } - case AdditiveBlending: + if ( blending !== CustomBlending ) { - if ( premultipliedAlpha ) { + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); + if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - } else { + gl.blendEquation( 32774 ); - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; - } - break; + } - case SubtractiveBlending: + if ( premultipliedAlpha ) { - if ( premultipliedAlpha ) { + switch ( blending ) { - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); + case NormalBlending: + gl.blendFuncSeparate( 1, 771, 1, 771 ); + break; - } else { + case AdditiveBlending: + gl.blendFunc( 1, 1 ); + break; - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); + case SubtractiveBlending: + gl.blendFuncSeparate( 0, 0, 769, 771 ); + break; - } - break; + case MultiplyBlending: + gl.blendFuncSeparate( 0, 768, 0, 770 ); + break; - case MultiplyBlending: + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - if ( premultipliedAlpha ) { + } - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); + } else { - } else { + switch ( blending ) { - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); + case NormalBlending: + gl.blendFuncSeparate( 770, 771, 1, 771 ); + break; - } - break; + case AdditiveBlending: + gl.blendFunc( 770, 1 ); + break; - default: + case SubtractiveBlending: + gl.blendFunc( 0, 769 ); + break; - if ( premultipliedAlpha ) { + case MultiplyBlending: + gl.blendFunc( 0, 768 ); + break; - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - } else { + } - gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); - gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + } - } + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; - } + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; } - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; + return; - } else { + } - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + // custom blending - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); - } + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + } - gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); - } + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; + currentPremultipledAlpha = null; } function setMaterial( material, frontFaceCW ) { material.side === DoubleSide - ? disable( gl.CULL_FACE ) - : enable( gl.CULL_FACE ); + ? disable( 2884 ) + : enable( 2884 ); var flipSided = ( material.side === BackSide ); if ( frontFaceCW ) flipSided = ! flipSided; setFlipSided( flipSided ); - material.transparent === true - ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) - : setBlending( NoBlending ); + ( 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 ); @@ -19373,11 +19927,11 @@ if ( flipSided ) { - gl.frontFace( gl.CW ); + gl.frontFace( 2304 ); } else { - gl.frontFace( gl.CCW ); + gl.frontFace( 2305 ); } @@ -19391,21 +19945,21 @@ if ( cullFace !== CullFaceNone ) { - enable( gl.CULL_FACE ); + enable( 2884 ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { - gl.cullFace( gl.BACK ); + gl.cullFace( 1029 ); } else if ( cullFace === CullFaceFront ) { - gl.cullFace( gl.FRONT ); + gl.cullFace( 1028 ); } else { - gl.cullFace( gl.FRONT_AND_BACK ); + gl.cullFace( 1032 ); } @@ -19413,7 +19967,7 @@ } else { - disable( gl.CULL_FACE ); + disable( 2884 ); } @@ -19437,7 +19991,7 @@ if ( polygonOffset ) { - enable( gl.POLYGON_OFFSET_FILL ); + enable( 32823 ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { @@ -19450,7 +20004,7 @@ } else { - disable( gl.POLYGON_OFFSET_FILL ); + disable( 32823 ); } @@ -19460,11 +20014,11 @@ if ( scissorTest ) { - enable( gl.SCISSOR_TEST ); + enable( 3089 ); } else { - disable( gl.SCISSOR_TEST ); + disable( 3089 ); } @@ -19474,7 +20028,7 @@ function activeTexture( webglSlot ) { - if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; + if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { @@ -19541,6 +20095,20 @@ } + function texImage3D() { + + try { + + gl.texImage3D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( 'THREE.WebGLState:', error ); + + } + + } + // function scissor( scissor ) { @@ -19580,7 +20148,7 @@ } - capabilities = {}; + enabledCapabilities = {}; compressedTextureFormats = null; @@ -19633,6 +20201,7 @@ bindTexture: bindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, + texImage3D: texImage3D, scissor: scissor, viewport: viewport, @@ -19649,66 +20218,77 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */ var _videoTextures = {}; var _canvas; // - function clampToMaxSize( image, maxSize ) { + var useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'; - if ( image.width > maxSize || image.height > maxSize ) { + function createCanvas( width, height ) { - if ( 'data' in image ) { + // Use OffscreenCanvas when available. Specially needed in web workers - console.warn( 'THREE.WebGLRenderer: image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - return; - - } + return useOffscreenCanvas ? + new OffscreenCanvas( width, height ) : + document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. + } - var scale = maxSize / Math.max( image.width, image.height ); + function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - canvas.width = Math.floor( image.width * scale ); - canvas.height = Math.floor( image.height * scale ); + var scale = 1; - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + // handle case if texture exceeds max size - console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + if ( image.width > maxSize || image.height > maxSize ) { - return canvas; + scale = maxSize / Math.max( image.width, image.height ); } - return image; + // only perform resize if necessary - } + if ( scale < 1 || needsPowerOfTwo === true ) { - function isPowerOfTwo( image ) { + // only perform resize for certain image types - return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - } + var floor = needsPowerOfTwo ? _Math.floorPowerOfTwo : Math.floor; - function makePowerOfTwo( image ) { + var width = floor( scale * image.width ); + var height = floor( scale * image.height ); - if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) { + if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + // cube textures can't reuse the same canvas - _canvas.width = _Math.floorPowerOfTwo( image.width ); - _canvas.height = _Math.floorPowerOfTwo( image.height ); + var canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - var context = _canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, _canvas.width, _canvas.height ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image ); + console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - return _canvas; + return canvas; + + } else { + + if ( 'data' in image ) { + + console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); + + } + + return image; + + } } @@ -19716,16 +20296,24 @@ } + function isPowerOfTwo( image ) { + + return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); + + } + function textureNeedsPowerOfTwo( texture ) { + if ( capabilities.isWebGL2 ) return false; + return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } - function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) { + function textureNeedsGenerateMipmaps( texture, supportsMips ) { - return texture.generateMipmaps && isPowerOfTwo && + return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } @@ -19741,17 +20329,62 @@ } + function getInternalFormat( glFormat, glType ) { + + if ( ! capabilities.isWebGL2 ) return glFormat; + + var 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' ); + + } else if ( internalFormat === 34843 || internalFormat === 34837 ) { + + console.warn( 'THREE.WebGLRenderer: Floating point textures with RGB format not supported. Please use RGBA instead.' ); + + } + + return internalFormat; + + } + // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { - return _gl.NEAREST; + return 9728; } - return _gl.LINEAR; + return 9729; } @@ -19793,23 +20426,10 @@ var textureProperties = properties.get( texture ); - if ( texture.image && textureProperties.__image__webglTextureCube ) { - - // cube texture + if ( textureProperties.__webglInit === undefined ) return; - _gl.deleteTexture( textureProperties.__image__webglTextureCube ); - - } else { - - // 2D texture - - if ( textureProperties.__webglInit === undefined ) return; - - _gl.deleteTexture( textureProperties.__webglTexture ); + _gl.deleteTexture( textureProperties.__webglTexture ); - } - - // remove all webgl properties properties.remove( texture ); } @@ -19856,7 +20476,31 @@ // + var textureUnits = 0; + + function resetTextureUnits() { + + textureUnits = 0; + + } + + function allocateTextureUnit() { + + var textureUnit = textureUnits; + + if ( textureUnit >= capabilities.maxTextures ) { + + console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + } + + textureUnits += 1; + + return textureUnit; + + } + + // function setTexture2D( texture, slot ) { @@ -19870,11 +20514,11 @@ if ( image === undefined ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); + 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', texture ); + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); } else { @@ -19885,33 +20529,57 @@ } - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 3553, textureProperties.__webglTexture ); } - function setTextureCube( texture, slot ) { + function setTexture2DArray( texture, slot ) { var textureProperties = properties.get( texture ); - if ( texture.image.length === 6 ) { + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + uploadTexture( textureProperties, texture, slot ); + return; - if ( ! textureProperties.__image__webglTextureCube ) { + } - texture.addEventListener( 'dispose', onTextureDispose ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 35866, textureProperties.__webglTexture ); - textureProperties.__image__webglTextureCube = _gl.createTexture(); + } - info.memory.textures ++; + function setTexture3D( texture, slot ) { - } + var 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 ); + + } - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + function setTextureCube( texture, slot ) { + + var textureProperties = properties.get( texture ); + + if ( texture.image.length === 6 ) { - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + initTexture( textureProperties, texture ); + + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); + + _gl.pixelStorei( 37440, texture.flipY ); var isCompressed = ( texture && texture.isCompressedTexture ); var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); @@ -19922,7 +20590,7 @@ if ( ! isCompressed && ! isDataTexture ) { - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); + cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, capabilities.maxCubemapSize ); } else { @@ -19933,11 +20601,12 @@ } var image = cubeImage[ 0 ], - isPowerOfTwoImage = isPowerOfTwo( image ), + supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2, glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ); + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( glFormat, glType ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); + setTextureParameters( 34067, texture, supportsMips ); for ( var i = 0; i < 6; i ++ ) { @@ -19945,11 +20614,11 @@ if ( isDataTexture ) { - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); } @@ -19965,7 +20634,7 @@ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { @@ -19975,7 +20644,7 @@ } else { - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } @@ -19995,10 +20664,10 @@ } - if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { // We assume images for cube map have the same size. - generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height ); + generateMipmap( 34067, texture, image.width, image.height ); } @@ -20008,8 +20677,8 @@ } else { - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, textureProperties.__webglTexture ); } @@ -20019,40 +20688,52 @@ function setTextureCubeDynamic( texture, slot ) { - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); + state.activeTexture( 33984 + slot ); + state.bindTexture( 34067, properties.get( texture ).__webglTexture ); } - function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { + function setTextureParameters( textureType, texture, supportsMips ) { var extension; - if ( isPowerOfTwoImage ) { + if ( supportsMips ) { - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) ); + _gl.texParameteri( textureType, 10242, utils.convert( texture.wrapS ) ); + _gl.texParameteri( textureType, 10243, utils.convert( texture.wrapT ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) ); + if ( textureType === 32879 || textureType === 35866 ) { + + _gl.texParameteri( textureType, 32882, utils.convert( texture.wrapR ) ); + + } + + _gl.texParameteri( textureType, 10240, utils.convert( texture.magFilter ) ); + _gl.texParameteri( textureType, 10241, utils.convert( texture.minFilter ) ); } else { - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _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.', texture ); + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); } - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + _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.', texture ); + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); } @@ -20063,7 +20744,7 @@ if ( extension ) { if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; + if ( texture.type === HalfFloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return; if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { @@ -20076,7 +20757,7 @@ } - function uploadTexture( textureProperties, texture, slot ) { + function initTexture( textureProperties, texture ) { if ( textureProperties.__webglInit === undefined ) { @@ -20090,26 +20771,33 @@ } - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + } - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); - _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + function uploadTexture( textureProperties, texture, slot ) { - var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); + var textureType = 3553; - if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { + if ( texture.isDataTexture2DArray ) textureType = 35866; + if ( texture.isDataTexture3D ) textureType = 32879; - image = makePowerOfTwo( image ); + 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 ); + + var needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; + var image = resizeImage( texture.image, needsPowerOfTwo, false, capabilities.maxTextureSize ); - var isPowerOfTwoImage = isPowerOfTwo( image ), + var supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2, glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ); + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( glFormat, glType ); - setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); + setTextureParameters( textureType, texture, supportsMips ); var mipmap, mipmaps = texture.mipmaps; @@ -20117,21 +20805,21 @@ // populate depth texture with dummy data - var internalFormat = _gl.DEPTH_COMPONENT; + glInternalFormat = 6402; if ( texture.type === FloatType ) { - if ( ! _isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' ); - internalFormat = _gl.DEPTH_COMPONENT32F; + if ( ! capabilities.isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' ); + glInternalFormat = 36012; - } else if ( _isWebGL2 ) { + } else if ( capabilities.isWebGL2 ) { // WebGL 2.0 requires signed internalformat for glTexImage2D - internalFormat = _gl.DEPTH_COMPONENT16; + glInternalFormat = 33189; } - if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { + 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 @@ -20151,7 +20839,7 @@ // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.format === DepthStencilFormat ) { - internalFormat = _gl.DEPTH_STENCIL; + 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. @@ -20167,7 +20855,7 @@ } - state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } else if ( texture.isDataTexture ) { @@ -20175,12 +20863,12 @@ // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels - if ( mipmaps.length > 0 && isPowerOfTwoImage ) { + if ( mipmaps.length > 0 && supportsMips ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } @@ -20189,7 +20877,7 @@ } else { - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); textureProperties.__maxMipLevel = 0; } @@ -20204,7 +20892,7 @@ if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { @@ -20214,7 +20902,7 @@ } else { - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } @@ -20222,6 +20910,16 @@ textureProperties.__maxMipLevel = mipmaps.length - 1; + } 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 ) { + + state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); + textureProperties.__maxMipLevel = 0; + } else { // regular Texture (image, video, canvas) @@ -20230,12 +20928,12 @@ // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels - if ( mipmaps.length > 0 && isPowerOfTwoImage ) { + if ( mipmaps.length > 0 && supportsMips ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); } @@ -20244,16 +20942,16 @@ } else { - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); + state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); textureProperties.__maxMipLevel = 0; } } - if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height ); + generateMipmap( 3553, texture, image.width, image.height ); } @@ -20270,36 +20968,73 @@ var glFormat = utils.convert( renderTarget.texture.format ); var glType = utils.convert( renderTarget.texture.type ); - state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + var glInternalFormat = getInternalFormat( glFormat, glType ); + state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + _gl.bindFramebuffer( 36160, framebuffer ); + _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); + _gl.bindFramebuffer( 36160, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget ) { + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + _gl.bindRenderbuffer( 36161, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( 36161, samples, 33189, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( 36161, 33189, renderTarget.width, renderTarget.height ); + + } + + _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( 36161, samples, 34041, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); + + } + + + _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); } else { - // FIXME: We don't support !depth !stencil - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + var glFormat = utils.convert( renderTarget.texture.format ); + var glType = utils.convert( renderTarget.texture.type ); + var glInternalFormat = getInternalFormat( glFormat, glType ); + + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + + } } - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindRenderbuffer( 36161, null ); } @@ -20309,7 +21044,7 @@ var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.bindFramebuffer( 36160, framebuffer ); if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { @@ -20334,11 +21069,11 @@ if ( renderTarget.depthTexture.format === DepthFormat ) { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); } else { @@ -20369,7 +21104,7 @@ for ( var i = 0; i < 6; i ++ ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); + _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); @@ -20377,7 +21112,7 @@ } else { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); @@ -20385,7 +21120,7 @@ } - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + _gl.bindFramebuffer( 36160, null ); } @@ -20402,7 +21137,8 @@ info.memory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); - var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); + var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); + var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2; // Setup framebuffer @@ -20420,42 +21156,78 @@ renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + if ( isMultisample ) { + + if ( capabilities.isWebGL2 ) { + + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + + _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); + var glFormat = utils.convert( renderTarget.texture.format ); + var glType = utils.convert( renderTarget.texture.type ); + var glInternalFormat = getInternalFormat( glFormat, glType ); + var samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + + _gl.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 ); + + } + + _gl.bindFramebuffer( 36160, null ); + + + } else { + + console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + + } + + } + } // Setup color buffer if ( isCube ) { - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); + state.bindTexture( 34067, textureProperties.__webglTexture ); + setTextureParameters( 34067, renderTarget.texture, supportsMips ); for ( var i = 0; i < 6; i ++ ) { - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); } - if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { + if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height ); + generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); } - state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + state.bindTexture( 34067, null ); } else { - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); + state.bindTexture( 3553, textureProperties.__webglTexture ); + setTextureParameters( 3553, renderTarget.texture, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); - if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) { + if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { - generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height ); + generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); } - state.bindTexture( _gl.TEXTURE_2D, null ); + state.bindTexture( 3553, null ); } @@ -20472,11 +21244,11 @@ function updateRenderTargetMipmap( renderTarget ) { var texture = renderTarget.texture; - var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); + var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2; - if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + var target = renderTarget.isWebGLRenderTargetCube ? 34067 : 3553; var webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); @@ -20487,6 +21259,43 @@ } + function updateMultisampleRenderTarget( renderTarget ) { + + if ( renderTarget.isWebGLMultisampleRenderTarget ) { + + if ( capabilities.isWebGL2 ) { + + var renderTargetProperties = properties.get( renderTarget ); + + _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); + + var width = renderTarget.width; + var height = renderTarget.height; + var mask = 16384; + + if ( renderTarget.depthBuffer ) mask |= 256; + if ( renderTarget.stencilBuffer ) mask |= 1024; + + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); + + } else { + + console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + + } + + } + + } + + function getRenderTargetSamples( renderTarget ) { + + return ( capabilities.isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? + Math.min( capabilities.maxSamples, renderTarget.samples ) : 0; + + } + function updateVideoTexture( texture ) { var id = texture.id; @@ -20503,11 +21312,80 @@ } + // backwards compatibility + + var warnedTexture2D = false; + var warnedTextureCube = false; + + function safeSetTexture2D( texture, slot ) { + + if ( texture && texture.isWebGLRenderTarget ) { + + if ( warnedTexture2D === false ) { + + console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." ); + warnedTexture2D = true; + + } + + texture = texture.texture; + + } + + setTexture2D( texture, slot ); + + } + + function safeSetTextureCube( texture, slot ) { + + if ( texture && texture.isWebGLRenderTargetCube ) { + + if ( warnedTextureCube === false ) { + + console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); + warnedTextureCube = true; + + } + + texture = texture.texture; + + } + + // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture + // TODO: unify these code paths + if ( ( texture && texture.isCubeTexture ) || + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + + // CompressedTexture can have Array in image :/ + + // this function alone should take care of cube textures + setTextureCube( texture, slot ); + + } else { + + // assumed: texture property of THREE.WebGLRenderTargetCube + setTextureCubeDynamic( texture, slot ); + + } + + } + + // + + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + + this.safeSetTexture2D = safeSetTexture2D; + this.safeSetTextureCube = safeSetTextureCube; } @@ -20515,68 +21393,71 @@ * @author thespite / http://www.twitter.com/thespite */ - function WebGLUtils( gl, extensions ) { + function WebGLUtils( gl, extensions, capabilities ) { function convert( p ) { var extension; - if ( p === RepeatWrapping ) return gl.REPEAT; - if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE; - if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT; + if ( p === RepeatWrapping ) return 10497; + if ( p === ClampToEdgeWrapping ) return 33071; + if ( p === MirroredRepeatWrapping ) return 33648; - if ( p === NearestFilter ) return gl.NEAREST; - if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST; - if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR; + if ( p === NearestFilter ) return 9728; + if ( p === NearestMipMapNearestFilter ) return 9984; + if ( p === NearestMipMapLinearFilter ) return 9986; - if ( p === LinearFilter ) return gl.LINEAR; - if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST; - if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR; + if ( p === LinearFilter ) return 9729; + if ( p === LinearMipMapNearestFilter ) return 9985; + if ( p === LinearMipMapLinearFilter ) return 9987; - if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; - if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; - if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; - if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5; + if ( p === UnsignedByteType ) return 5121; + if ( p === UnsignedShort4444Type ) return 32819; + if ( p === UnsignedShort5551Type ) return 32820; + if ( p === UnsignedShort565Type ) return 33635; - if ( p === ByteType ) return gl.BYTE; - if ( p === ShortType ) return gl.SHORT; - if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; - if ( p === IntType ) return gl.INT; - if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; - if ( p === FloatType ) return gl.FLOAT; + 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 ( capabilities.isWebGL2 ) return 5131; + extension = extensions.get( 'OES_texture_half_float' ); if ( extension !== null ) return extension.HALF_FLOAT_OES; } - if ( p === AlphaFormat ) return gl.ALPHA; - if ( p === RGBFormat ) return gl.RGB; - if ( p === RGBAFormat ) return gl.RGBA; - if ( p === LuminanceFormat ) return gl.LUMINANCE; - if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; - if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; - if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; + 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; - if ( p === AddEquation ) return gl.FUNC_ADD; - if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT; - if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT; + if ( p === AddEquation ) return 32774; + if ( p === SubtractEquation ) return 32778; + if ( p === ReverseSubtractEquation ) return 32779; - if ( p === ZeroFactor ) return gl.ZERO; - if ( p === OneFactor ) return gl.ONE; - if ( p === SrcColorFactor ) return gl.SRC_COLOR; - if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR; - if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA; - if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA; - if ( p === DstAlphaFactor ) return gl.DST_ALPHA; - if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA; + if ( p === ZeroFactor ) return 0; + if ( p === OneFactor ) return 1; + if ( p === SrcColorFactor ) return 768; + if ( p === OneMinusSrcColorFactor ) return 769; + if ( p === SrcAlphaFactor ) return 770; + if ( p === OneMinusSrcAlphaFactor ) return 771; + if ( p === DstAlphaFactor ) return 772; + if ( p === OneMinusDstAlphaFactor ) return 773; - if ( p === DstColorFactor ) return gl.DST_COLOR; - if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR; - if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE; + if ( p === DstColorFactor ) return 774; + if ( p === OneMinusDstColorFactor ) return 775; + if ( p === SrcAlphaSaturateFactor ) return 776; if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { @@ -20636,6 +21517,13 @@ if ( p === MinEquation || p === MaxEquation ) { + if ( capabilities.isWebGL2 ) { + + if ( p === MinEquation ) return 32775; + if ( p === MaxEquation ) return 32776; + + } + extension = extensions.get( 'EXT_blend_minmax' ); if ( extension !== null ) { @@ -20649,6 +21537,8 @@ if ( p === UnsignedInt248Type ) { + if ( capabilities.isWebGL2 ) return 34042; + extension = extensions.get( 'WEBGL_depth_texture' ); if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; @@ -20663,6 +21553,97 @@ } + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function Group() { + + Object3D.call( this ); + + this.type = 'Group'; + + } + + Group.prototype = Object.assign( Object.create( Object3D.prototype ), { + + constructor: Group, + + isGroup: true + + } ); + + /** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley + */ + + function Camera() { + + Object3D.call( this ); + + this.type = 'Camera'; + + this.matrixWorldInverse = new Matrix4(); + + this.projectionMatrix = new Matrix4(); + this.projectionMatrixInverse = new Matrix4(); + + } + + Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { + + constructor: Camera, + + isCamera: true, + + copy: function ( source, recursive ) { + + Object3D.prototype.copy.call( this, source, recursive ); + + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + + this.projectionMatrix.copy( source.projectionMatrix ); + this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); + + return this; + + }, + + getWorldDirection: function ( target ) { + + if ( target === undefined ) { + + console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); + target = new Vector3(); + + } + + this.updateMatrixWorld( true ); + + var e = this.matrixWorld.elements; + + return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); + + }, + + updateMatrixWorld: function ( force ) { + + Object3D.prototype.updateMatrixWorld.call( this, force ); + + this.matrixWorldInverse.getInverse( this.matrixWorld ); + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + + } ); + /** * @author mrdoob / http://mrdoob.com/ * @author greggman / http://games.greggman.com/ @@ -20791,17 +21772,17 @@ * var fullHeight = h * 2; * * --A-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ @@ -20850,8 +21831,7 @@ updateProjectionMatrix: function () { var near = this.near, - top = near * Math.tan( - _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, + top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, height = 2 * top, width = this.aspect * height, left = - 0.5 * width, @@ -20874,6 +21854,8 @@ this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); + this.projectionMatrixInverse.getInverse( this.projectionMatrix ); + }, toJSON: function ( meta ) { @@ -20920,6 +21902,69 @@ } ); + /** + * @author jsantell / https://www.jsantell.com/ + * @author mrdoob / http://mrdoob.com/ + */ + + var cameraLPos = new Vector3(); + var 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 ) { + + cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); + cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); + + var ipd = cameraLPos.distanceTo( cameraRPos ); + + var projL = cameraL.projectionMatrix.elements; + var projR = cameraR.projectionMatrix.elements; + + // 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. + var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; + var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; + + var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + var left = near * leftFov; + var right = near * rightFov; + + // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + var zOffset = ipd / ( - leftFov + rightFov ); + var xOffset = zOffset * - leftFov; + + // 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.getInverse( camera.matrixWorld ); + + // 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. + var near2 = near + zOffset; + var far2 = far + zOffset; + var left2 = left - xOffset; + var right2 = right + ( ipd - xOffset ); + var top2 = topFov * far / far2 * near2; + var bottom2 = bottomFov * far / far2 * near2; + + camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + + } + /** * @author mrdoob / http://mrdoob.com/ */ @@ -20933,9 +21978,14 @@ var poseTarget = null; + var controllers = []; var standingMatrix = new Matrix4(); var standingMatrixInverse = new Matrix4(); + var framebufferScaleFactor = 1.0; + + var frameOfReferenceType = 'stage'; + if ( typeof window !== 'undefined' && 'VRFrameData' in window ) { frameData = new window.VRFrameData(); @@ -20967,24 +22017,116 @@ } - var currentSize, currentPixelRatio; + var currentSize = new Vector2(), currentPixelRatio; function onVRDisplayPresentChange() { if ( isPresenting() ) { var eyeParameters = device.getEyeParameters( 'left' ); - var renderWidth = eyeParameters.renderWidth; - var renderHeight = eyeParameters.renderHeight; + var renderWidth = eyeParameters.renderWidth * framebufferScaleFactor; + var renderHeight = eyeParameters.renderHeight * framebufferScaleFactor; currentPixelRatio = renderer.getPixelRatio(); - currentSize = renderer.getSize(); + renderer.getSize( currentSize ); renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 ); - } else if ( scope.enabled ) { + animation.start(); + + } else { + + if ( scope.enabled ) { - renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); + renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); + + } + + animation.stop(); + + } + + } + + // + + var triggers = []; + + function findGamepad( id ) { + + var gamepads = navigator.getGamepads && navigator.getGamepads(); + + for ( var i = 0, j = 0, l = gamepads.length; i < l; i ++ ) { + + var gamepad = gamepads[ i ]; + + if ( gamepad && ( gamepad.id === 'Daydream Controller' || + gamepad.id === 'Gear VR Controller' || gamepad.id === 'Oculus Go Controller' || + gamepad.id === 'OpenVR Gamepad' || gamepad.id.startsWith( 'Oculus Touch' ) || + gamepad.id.startsWith( 'Spatial Controller' ) ) ) { + + if ( j === id ) return gamepad; + + j ++; + + } + + } + + } + + function updateControllers() { + + for ( var i = 0; i < controllers.length; i ++ ) { + + var controller = controllers[ i ]; + + var gamepad = findGamepad( i ); + + if ( gamepad !== undefined && gamepad.pose !== undefined ) { + + if ( gamepad.pose === null ) return; + + // Pose + + var pose = gamepad.pose; + + if ( pose.hasPosition === false ) controller.position.set( 0.2, - 0.6, - 0.05 ); + + if ( pose.position !== null ) controller.position.fromArray( pose.position ); + if ( pose.orientation !== null ) controller.quaternion.fromArray( pose.orientation ); + controller.matrix.compose( controller.position, controller.quaternion, controller.scale ); + controller.matrix.premultiply( standingMatrix ); + controller.matrix.decompose( controller.position, controller.quaternion, controller.scale ); + controller.matrixWorldNeedsUpdate = true; + controller.visible = true; + + // Trigger + + var buttonId = gamepad.id === 'Daydream Controller' ? 0 : 1; + + if ( triggers[ i ] !== gamepad.buttons[ buttonId ].pressed ) { + + triggers[ i ] = gamepad.buttons[ buttonId ].pressed; + + if ( triggers[ i ] === true ) { + + controller.dispatchEvent( { type: 'selectstart' } ); + + } else { + + controller.dispatchEvent( { type: 'selectend' } ); + controller.dispatchEvent( { type: 'select' } ); + + } + + } + + } else { + + controller.visible = false; + + } } @@ -20993,7 +22135,24 @@ // this.enabled = false; - this.userHeight = 1.6; + + this.getController = function ( id ) { + + var controller = controllers[ id ]; + + if ( controller === undefined ) { + + controller = new Group(); + controller.matrixAutoUpdate = false; + controller.visible = false; + + controllers[ id ] = controller; + + } + + return controller; + + }; this.getDevice = function () { @@ -21005,6 +22164,20 @@ if ( value !== undefined ) device = value; + animation.setContext( value ); + + }; + + this.setFramebufferScaleFactor = function ( value ) { + + framebufferScaleFactor = value; + + }; + + this.setFrameOfReferenceType = function ( value ) { + + frameOfReferenceType = value; + }; this.setPoseTarget = function ( object ) { @@ -21015,7 +22188,16 @@ this.getCamera = function ( camera ) { - if ( device === null ) return camera; + var userHeight = frameOfReferenceType === 'stage' ? 1.6 : 0; + + if ( isPresenting() === false ) { + + camera.position.set( 0, userHeight, 0 ); + camera.rotation.set( 0, 0, 0 ); + + return camera; + + } device.depthNear = camera.near; device.depthFar = camera.far; @@ -21024,15 +22206,19 @@ // - var stageParameters = device.stageParameters; + if ( frameOfReferenceType === 'stage' ) { - if ( stageParameters ) { + var stageParameters = device.stageParameters; - standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); + if ( stageParameters ) { - } else { + standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); - standingMatrix.makeTranslation( 0, scope.userHeight, 0 ); + } else { + + standingMatrix.makeTranslation( 0, userHeight, 0 ); + + } } @@ -21062,8 +22248,6 @@ poseObject.updateMatrixWorld(); - if ( device.isPresenting === false ) return camera; - // cameraL.near = camera.near; @@ -21072,9 +22256,6 @@ cameraL.far = camera.far; cameraR.far = camera.far; - cameraVR.matrixWorld.copy( camera.matrixWorld ); - cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse ); - cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); @@ -21082,8 +22263,12 @@ standingMatrixInverse.getInverse( standingMatrix ); - cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); - cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); + if ( frameOfReferenceType === 'stage' ) { + + cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); + cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); + + } var parent = poseObject.parent; @@ -21104,10 +22289,7 @@ cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); - // HACK (mrdoob) - // https://github.com/w3c/webvr/issues/203 - - cameraVR.projectionMatrix.copy( cameraL.projectionMatrix ); + setProjectionFromUnion( cameraVR, cameraL, cameraR ); // @@ -21131,6 +22313,8 @@ } + updateControllers(); + return cameraVR; }; @@ -21141,6 +22325,20 @@ }; + this.isPresenting = isPresenting; + + // Animation Loop + + var animation = new WebGLAnimation(); + + this.setAnimationLoop = function ( callback ) { + + animation.setAnimationLoop( callback ); + + if ( isPresenting() ) animation.start(); + + }; + this.submitFrame = function () { if ( isPresenting() ) device.submitFrame(); @@ -21159,6 +22357,312 @@ } + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function WebXRManager( renderer ) { + + var gl = renderer.context; + + var device = null; + var session = null; + + var framebufferScaleFactor = 1.0; + + var frameOfReference = null; + var frameOfReferenceType = 'stage'; + + var pose = null; + + var controllers = []; + var inputSources = []; + + function isPresenting() { + + return session !== null && frameOfReference !== null; + + } + + // + + var cameraL = new PerspectiveCamera(); + cameraL.layers.enable( 1 ); + cameraL.viewport = new Vector4(); + + var cameraR = new PerspectiveCamera(); + cameraR.layers.enable( 2 ); + cameraR.viewport = new Vector4(); + + var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); + cameraVR.layers.enable( 1 ); + cameraVR.layers.enable( 2 ); + + // + + this.enabled = false; + + this.getController = function ( id ) { + + var controller = controllers[ id ]; + + if ( controller === undefined ) { + + controller = new Group(); + controller.matrixAutoUpdate = false; + controller.visible = false; + + controllers[ id ] = controller; + + } + + return controller; + + }; + + this.getDevice = function () { + + return device; + + }; + + this.setDevice = function ( value ) { + + if ( value !== undefined ) device = value; + if ( value instanceof XRDevice ) gl.setCompatibleXRDevice( value ); + + }; + + // + + function onSessionEvent( event ) { + + var controller = controllers[ inputSources.indexOf( event.inputSource ) ]; + if ( controller ) controller.dispatchEvent( { type: event.type } ); + + } + + function onSessionEnd() { + + renderer.setFramebuffer( null ); + renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 + animation.stop(); + + } + + this.setFramebufferScaleFactor = function ( value ) { + + framebufferScaleFactor = value; + + }; + + this.setFrameOfReferenceType = function ( value ) { + + frameOfReferenceType = value; + + }; + + this.setSession = function ( value ) { + + session = value; + + if ( session !== null ) { + + session.addEventListener( 'select', onSessionEvent ); + session.addEventListener( 'selectstart', onSessionEvent ); + session.addEventListener( 'selectend', onSessionEvent ); + session.addEventListener( 'end', onSessionEnd ); + + session.baseLayer = new XRWebGLLayer( session, gl, { framebufferScaleFactor: framebufferScaleFactor } ); + session.requestFrameOfReference( frameOfReferenceType ).then( function ( value ) { + + frameOfReference = value; + + renderer.setFramebuffer( session.baseLayer.framebuffer ); + + animation.setContext( session ); + animation.start(); + + } ); + + // + + inputSources = session.getInputSources(); + + session.addEventListener( 'inputsourceschange', function () { + + inputSources = session.getInputSources(); + console.log( inputSources ); + + for ( var i = 0; i < controllers.length; i ++ ) { + + var controller = controllers[ i ]; + controller.userData.inputSource = inputSources[ i ]; + + } + + } ); + + } + + }; + + function updateCamera( camera, parent ) { + + if ( parent === null ) { + + camera.matrixWorld.copy( camera.matrix ); + + } else { + + camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); + + } + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + } + + this.getCamera = function ( camera ) { + + if ( isPresenting() ) { + + var parent = camera.parent; + var cameras = cameraVR.cameras; + + updateCamera( cameraVR, parent ); + + for ( var i = 0; i < cameras.length; i ++ ) { + + updateCamera( cameras[ i ], parent ); + + } + + // update camera and its children + + camera.matrixWorld.copy( cameraVR.matrixWorld ); + + var children = camera.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + children[ i ].updateMatrixWorld( true ); + + } + + setProjectionFromUnion( cameraVR, cameraL, cameraR ); + + return cameraVR; + + } + + return camera; + + }; + + this.isPresenting = isPresenting; + + // Animation Loop + + var onAnimationFrameCallback = null; + + function onAnimationFrame( time, frame ) { + + pose = frame.getDevicePose( frameOfReference ); + + if ( pose !== null ) { + + var layer = session.baseLayer; + var views = frame.views; + + for ( var i = 0; i < views.length; i ++ ) { + + var view = views[ i ]; + var viewport = layer.getViewport( view ); + var viewMatrix = pose.getViewMatrix( view ); + + var camera = cameraVR.cameras[ i ]; + camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix ); + camera.projectionMatrix.fromArray( view.projectionMatrix ); + camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); + + if ( i === 0 ) { + + cameraVR.matrix.copy( camera.matrix ); + + } + + } + + } + + // + + for ( var i = 0; i < controllers.length; i ++ ) { + + var controller = controllers[ i ]; + + var inputSource = inputSources[ i ]; + + if ( inputSource ) { + + var inputPose = frame.getInputPose( inputSource, frameOfReference ); + + if ( inputPose !== null ) { + + if ( 'targetRay' in inputPose ) { + + controller.matrix.elements = inputPose.targetRay.transformMatrix; + + } else if ( 'pointerMatrix' in inputPose ) { + + // DEPRECATED + + controller.matrix.elements = inputPose.pointerMatrix; + + } + + controller.matrix.decompose( controller.position, controller.rotation, controller.scale ); + controller.visible = true; + + continue; + + } + + } + + controller.visible = false; + + } + + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + + } + + var animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); + + this.setAnimationLoop = function ( callback ) { + + onAnimationFrameCallback = callback; + + }; + + this.dispose = function () {}; + + // DEPRECATED + + this.getStandingMatrix = function () { + + console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' ); + return new Matrix4(); + + }; + + this.submitFrame = function () {}; + + } + /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ @@ -21192,6 +22696,16 @@ this.domElement = _canvas; this.context = null; + // Debug configuration container + this.debug = { + + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: false + }; + // clearing this.autoClear = true; @@ -21237,10 +22751,19 @@ // internal state cache + _framebuffer = null, + _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, - _currentGeometryProgram = '', + + // geometry and program caching + + _currentGeometryProgram = { + geometry: null, + program: null, + wireframe: false + }, _currentCamera = null, _currentArrayCamera = null, @@ -21251,10 +22774,6 @@ // - _usedTextureUnits = 0, - - // - _width = _canvas.width, _height = _canvas.height, @@ -21338,6 +22857,7 @@ } catch ( error ) { console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; } @@ -21346,27 +22866,32 @@ var programCache, renderLists, renderStates; var background, morphtargets, bufferRenderer, indexedBufferRenderer; - var spriteRenderer; var utils; function initGLContext() { extensions = new WebGLExtensions( _gl ); - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_float_linear' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'OES_element_index_uint' ); - extensions.get( 'ANGLE_instanced_arrays' ); - - utils = new WebGLUtils( _gl, extensions ); capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - state = new WebGLState( _gl, extensions, utils ); + if ( ! capabilities.isWebGL2 ) { + + extensions.get( 'WEBGL_depth_texture' ); + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_half_float' ); + extensions.get( 'OES_texture_half_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); + extensions.get( 'OES_element_index_uint' ); + extensions.get( 'ANGLE_instanced_arrays' ); + + } + + extensions.get( 'OES_texture_float_linear' ); + + utils = new WebGLUtils( _gl, extensions, capabilities ); + + state = new WebGLState( _gl, extensions, utils, capabilities ); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); @@ -21377,16 +22902,14 @@ geometries = new WebGLGeometries( _gl, attributes, info ); objects = new WebGLObjects( geometries, info ); morphtargets = new WebGLMorphtargets( _gl ); - programCache = new WebGLPrograms( _this, extensions, capabilities ); + programCache = new WebGLPrograms( _this, extensions, capabilities, textures ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(); - background = new WebGLBackground( _this, state, geometries, _premultipliedAlpha ); + background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info ); - - spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities ); + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); info.programs = programCache.programs; @@ -21404,7 +22927,7 @@ // vr - var vr = new WebVRManager( _this ); + var vr = ( typeof navigator !== 'undefined' && 'xr' in navigator ) ? new WebXRManager( _this ) : new WebVRManager( _this ); this.vr = vr; @@ -21458,20 +22981,23 @@ }; - this.getSize = function () { + this.getSize = function ( target ) { - return { - width: _width, - height: _height - }; + if ( target === undefined ) { + + console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); + + target = new Vector2(); + + } + + return target.set( _width, _height ); }; this.setSize = function ( width, height, updateStyle ) { - var device = vr.getDevice(); - - if ( device && device.isPresenting ) { + if ( vr.isPresenting() ) { console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); return; @@ -21495,12 +23021,17 @@ }; - this.getDrawingBufferSize = function () { + this.getDrawingBufferSize = function ( target ) { - return { - width: _width * _pixelRatio, - height: _height * _pixelRatio - }; + if ( target === undefined ) { + + console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); + + target = new Vector2(); + + } + + return target.set( _width * _pixelRatio, _height * _pixelRatio ); }; @@ -21518,26 +23049,70 @@ }; - this.getCurrentViewport = function () { + this.getCurrentViewport = function ( target ) { + + if ( target === undefined ) { + + console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); + + target = new Vector4(); + + } + + return target.copy( _currentViewport ); + + }; + + this.getViewport = function ( target ) { - return _currentViewport; + return target.copy( _viewport ); }; this.setViewport = function ( x, y, width, height ) { - _viewport.set( x, _height - y - height, width, height ); + if ( x.isVector4 ) { + + _viewport.set( x.x, x.y, x.z, x.w ); + + } else { + + _viewport.set( x, y, width, height ); + + } + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); }; + this.getScissor = function ( target ) { + + return target.copy( _scissor ); + + }; + this.setScissor = function ( x, y, width, height ) { - _scissor.set( x, _height - y - height, width, height ); + if ( x.isVector4 ) { + + _scissor.set( x.x, x.y, x.z, x.w ); + + } else { + + _scissor.set( x, y, width, height ); + + } + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); }; + this.getScissorTest = function () { + + return _scissorTest; + + }; + this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); @@ -21574,9 +23149,9 @@ var bits = 0; - if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; - if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; - if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + if ( color === undefined || color ) bits |= 16384; + if ( depth === undefined || depth ) bits |= 256; + if ( stencil === undefined || stencil ) bits |= 1024; _gl.clear( bits ); @@ -21600,13 +23175,6 @@ }; - this.clearTarget = function ( renderTarget, color, depth, stencil ) { - - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); - - }; - // this.dispose = function () { @@ -21621,7 +23189,7 @@ vr.dispose(); - stopAnimation(); + animation.stop(); }; @@ -21684,17 +23252,17 @@ // Buffer rendering - function renderObjectImmediate( object, program, material ) { + function renderObjectImmediate( object, program ) { object.render( function ( object ) { - _this.renderBufferImmediate( object, program, material ); + _this.renderBufferImmediate( object, program ); } ); } - this.renderBufferImmediate = function ( object, program, material ) { + this.renderBufferImmediate = function ( object, program ) { state.initAttributes(); @@ -21709,80 +23277,47 @@ if ( object.hasPositions ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + _gl.bindBuffer( 34962, buffers.position ); + _gl.bufferData( 34962, object.positionArray, 35048 ); state.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); } if ( object.hasNormals ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); - - if ( ! material.isMeshPhongMaterial && - ! material.isMeshStandardMaterial && - ! material.isMeshNormalMaterial && - material.flatShading === true ) { - - for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { - - var array = object.normalArray; - - var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; - var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; - var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; - - array[ i + 0 ] = nx; - array[ i + 1 ] = ny; - array[ i + 2 ] = nz; - - array[ i + 3 ] = nx; - array[ i + 4 ] = ny; - array[ i + 5 ] = nz; - - array[ i + 6 ] = nx; - array[ i + 7 ] = ny; - array[ i + 8 ] = nz; - - } - - } - - _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + _gl.bindBuffer( 34962, buffers.normal ); + _gl.bufferData( 34962, object.normalArray, 35048 ); state.enableAttribute( programAttributes.normal ); - - _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); } - if ( object.hasUvs && material.map ) { + if ( object.hasUvs ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + _gl.bindBuffer( 34962, buffers.uv ); + _gl.bufferData( 34962, object.uvArray, 35048 ); state.enableAttribute( programAttributes.uv ); - - _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); } - if ( object.hasColors && material.vertexColors !== NoColors ) { + if ( object.hasColors ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + _gl.bindBuffer( 34962, buffers.color ); + _gl.bufferData( 34962, object.colorArray, 35048 ); state.enableAttribute( programAttributes.color ); - - _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 ); + _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); } state.disableUnusedAttributes(); - _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + _gl.drawArrays( 4, 0, object.count ); object.count = 0; @@ -21795,13 +23330,16 @@ state.setMaterial( material, frontFaceCW ); var program = setProgram( camera, fog, material, object ); - var geometryProgram = geometry.id + '_' + program.id + '_' + ( material.wireframe === true ); var updateBuffers = false; - if ( geometryProgram !== _currentGeometryProgram ) { + if ( _currentGeometryProgram.geometry !== geometry.id || + _currentGeometryProgram.program !== program.id || + _currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) { - _currentGeometryProgram = geometryProgram; + _currentGeometryProgram.geometry = geometry.id; + _currentGeometryProgram.program = program.id; + _currentGeometryProgram.wireframe = material.wireframe === true; updateBuffers = true; } @@ -21845,7 +23383,7 @@ if ( index !== null ) { - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer ); + _gl.bindBuffer( 34963, attribute.buffer ); } @@ -21885,22 +23423,22 @@ if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( _gl.LINES ); + renderer.setMode( 1 ); } else { switch ( object.drawMode ) { case TrianglesDrawMode: - renderer.setMode( _gl.TRIANGLES ); + renderer.setMode( 4 ); break; case TriangleStripDrawMode: - renderer.setMode( _gl.TRIANGLE_STRIP ); + renderer.setMode( 5 ); break; case TriangleFanDrawMode: - renderer.setMode( _gl.TRIANGLE_FAN ); + renderer.setMode( 6 ); break; } @@ -21918,21 +23456,25 @@ if ( object.isLineSegments ) { - renderer.setMode( _gl.LINES ); + renderer.setMode( 1 ); } else if ( object.isLineLoop ) { - renderer.setMode( _gl.LINE_LOOP ); + renderer.setMode( 2 ); } else { - renderer.setMode( _gl.LINE_STRIP ); + renderer.setMode( 3 ); } } else if ( object.isPoints ) { - renderer.setMode( _gl.POINTS ); + renderer.setMode( 0 ); + + } else if ( object.isSprite ) { + + renderer.setMode( 4 ); } @@ -21954,7 +23496,7 @@ function setupVertexAttributes( material, program, geometry ) { - if ( geometry && geometry.isInstancedBufferGeometry ) { + if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { @@ -22018,7 +23560,7 @@ } - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.bindBuffer( 34962, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); } else { @@ -22039,7 +23581,7 @@ } - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.bindBuffer( 34962, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); } @@ -22132,61 +23674,48 @@ // Animation Loop - var isAnimating = false; - var onAnimationFrame = null; - - function startAnimation() { - - if ( isAnimating ) return; - - requestAnimationLoopFrame(); - - isAnimating = true; - - } + var onAnimationFrameCallback = null; - function stopAnimation() { + function onAnimationFrame( time ) { - isAnimating = false; + if ( vr.isPresenting() ) return; + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } - function requestAnimationLoopFrame() { + var animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - var device = vr.getDevice(); + if ( typeof window !== 'undefined' ) animation.setContext( window ); - if ( device && device.isPresenting ) { + this.setAnimationLoop = function ( callback ) { - device.requestAnimationFrame( animationLoop ); + onAnimationFrameCallback = callback; + vr.setAnimationLoop( callback ); - } else { - - window.requestAnimationFrame( animationLoop ); - - } + animation.start(); - } - - function animationLoop( time ) { + }; - if ( isAnimating === false ) return; + // Rendering - onAnimationFrame( time ); + this.render = function ( scene, camera ) { - requestAnimationLoopFrame(); + var renderTarget, forceClear; - } + if ( arguments[ 2 ] !== undefined ) { - this.animate = function ( callback ) { + console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); + renderTarget = arguments[ 2 ]; - onAnimationFrame = callback; - onAnimationFrame !== null ? startAnimation() : stopAnimation(); + } - }; + if ( arguments[ 3 ] !== undefined ) { - // Rendering + console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); + forceClear = arguments[ 3 ]; - this.render = function ( scene, camera, renderTarget, forceClear ) { + } if ( ! ( camera && camera.isCamera ) ) { @@ -22199,7 +23728,9 @@ // reset caching for this frame - _currentGeometryProgram = ''; + _currentGeometryProgram.geometry = null; + _currentGeometryProgram.program = null; + _currentGeometryProgram.wireframe = false; _currentMaterialId = - 1; _currentCamera = null; @@ -22222,7 +23753,7 @@ currentRenderState = renderStates.get( scene, camera ); currentRenderState.init(); - scene.onBeforeRender( _this, scene, camera, renderTarget ); + scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); @@ -22233,7 +23764,7 @@ currentRenderList = renderLists.get( scene, camera ); currentRenderList.init(); - projectObject( scene, camera, _this.sortObjects ); + projectObject( scene, camera, 0, _this.sortObjects ); if ( _this.sortObjects === true ) { @@ -22257,14 +23788,12 @@ if ( this.info.autoReset ) this.info.reset(); - if ( renderTarget === undefined ) { + if ( renderTarget !== undefined ) { - renderTarget = null; + this.setRenderTarget( renderTarget ); } - this.setRenderTarget( renderTarget ); - // background.render( currentRenderList, scene, camera, forceClear ); @@ -22293,17 +23822,21 @@ } - // custom renderers + // - var spritesArray = currentRenderState.state.spritesArray; + scene.onAfterRender( _this, scene, camera ); + + // - spriteRenderer.render( spritesArray, scene, camera ); + if ( _currentRenderTarget !== null ) { - // Generate mipmap if we're using any kind of mipmap filtering + // Generate mipmap if we're using any kind of mipmap filtering - if ( renderTarget ) { + textures.updateRenderTargetMipmap( _currentRenderTarget ); - textures.updateRenderTargetMipmap( renderTarget ); + // resolve multisample renderbuffers to a single-sample texture if necessary + + textures.updateMultisampleRenderTarget( _currentRenderTarget ); } @@ -22315,8 +23848,6 @@ state.setPolygonOffset( false ); - scene.onAfterRender( _this, scene, camera ); - if ( vr.enabled ) { vr.submitFrame(); @@ -22330,62 +23861,7 @@ }; - /* - // TODO Duplicated code (Frustum) - - var _sphere = new Sphere(); - - function isObjectViewable( object ) { - - var geometry = object.geometry; - - if ( geometry.boundingSphere === null ) - geometry.computeBoundingSphere(); - - _sphere.copy( geometry.boundingSphere ). - applyMatrix4( object.matrixWorld ); - - return isSphereViewable( _sphere ); - - } - - function isSpriteViewable( sprite ) { - - _sphere.center.set( 0, 0, 0 ); - _sphere.radius = 0.7071067811865476; - _sphere.applyMatrix4( sprite.matrixWorld ); - - return isSphereViewable( _sphere ); - - } - - function isSphereViewable( sphere ) { - - if ( ! _frustum.intersectsSphere( sphere ) ) return false; - - var numPlanes = _clipping.numPlanes; - - if ( numPlanes === 0 ) return true; - - var planes = _this.clippingPlanes, - - center = sphere.center, - negRad = - sphere.radius, - i = 0; - - do { - - // out when deeper than radius in the negative halfspace - if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; - - } while ( ++ i !== numPlanes ); - - return true; - - } - */ - - function projectObject( object, camera, sortObjects ) { + function projectObject( object, camera, groupOrder, sortObjects ) { if ( object.visible === false ) return; @@ -22393,7 +23869,11 @@ if ( visible ) { - if ( object.isLight ) { + if ( object.isGroup ) { + + groupOrder = object.renderOrder; + + } else if ( object.isLight ) { currentRenderState.pushLight( object ); @@ -22407,7 +23887,21 @@ if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - currentRenderState.pushSprite( object ); + if ( sortObjects ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); + + } + + var geometry = objects.update( object ); + var material = object.material; + + if ( material.visible ) { + + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + + } } @@ -22420,7 +23914,7 @@ } - currentRenderList.push( object, null, object.material, _vector3.z, null ); + currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); } else if ( object.isMesh || object.isLine || object.isPoints ) { @@ -22453,7 +23947,7 @@ if ( groupMaterial && groupMaterial.visible ) { - currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group ); + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); } @@ -22461,7 +23955,7 @@ } else if ( material.visible ) { - currentRenderList.push( object, geometry, material, _vector3.z, null ); + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); } @@ -22475,7 +23969,7 @@ for ( var i = 0, l = children.length; i < l; i ++ ) { - projectObject( children[ i ], camera, sortObjects ); + projectObject( children[ i ], camera, groupOrder, sortObjects ); } @@ -22504,14 +23998,24 @@ if ( object.layers.test( camera2.layers ) ) { - var bounds = camera2.bounds; + if ( 'viewport' in camera2 ) { // XR + + state.viewport( _currentViewport.copy( camera2.viewport ) ); + + } else { + + var bounds = camera2.bounds; - var x = bounds.x * _width; - var y = bounds.y * _height; - var width = bounds.z * _width; - var height = bounds.w * _height; + var x = bounds.x * _width; + var y = bounds.y * _height; + var width = bounds.z * _width; + var height = bounds.w * _height; - state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); + state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) ); + + } + + currentRenderState.setupLights( camera2 ); renderObject( object, scene, camera2, geometry, material, group ); @@ -22541,15 +24045,15 @@ if ( object.isImmediateRenderObject ) { - var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - - state.setMaterial( material, frontFaceCW ); + state.setMaterial( material ); var program = setProgram( camera, scene.fog, material, object ); - _currentGeometryProgram = ''; + _currentGeometryProgram.geometry = null; + _currentGeometryProgram.program = null; + _currentGeometryProgram.wireframe = false; - renderObjectImmediate( object, program, material ); + renderObjectImmediate( object, program ); } else { @@ -22569,6 +24073,9 @@ var lights = currentRenderState.state.lights; var shadowsArray = currentRenderState.state.shadowsArray; + var lightsHash = materialProperties.lightsHash; + var lightsStateHash = lights.state.hash; + var parameters = programCache.getParameters( material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); @@ -22587,9 +24094,22 @@ // changed glsl or parameters releaseMaterialProgramReference( material ); - } else if ( materialProperties.lightsHash !== lights.state.hash ) { + } else if ( lightsHash.stateID !== lightsStateHash.stateID || + lightsHash.directionalLength !== lightsStateHash.directionalLength || + lightsHash.pointLength !== lightsStateHash.pointLength || + lightsHash.spotLength !== lightsStateHash.spotLength || + lightsHash.rectAreaLength !== lightsStateHash.rectAreaLength || + lightsHash.hemiLength !== lightsStateHash.hemiLength || + lightsHash.shadowsLength !== lightsStateHash.shadowsLength ) { + + lightsHash.stateID = lightsStateHash.stateID; + lightsHash.directionalLength = lightsStateHash.directionalLength; + lightsHash.pointLength = lightsStateHash.pointLength; + lightsHash.spotLength = lightsStateHash.spotLength; + lightsHash.rectAreaLength = lightsStateHash.rectAreaLength; + lightsHash.hemiLength = lightsStateHash.hemiLength; + lightsHash.shadowsLength = lightsStateHash.shadowsLength; - properties.update( material, 'lightsHash', lights.state.hash ); programChange = false; } else if ( parameters.shaderID !== undefined ) { @@ -22612,7 +24132,7 @@ materialProperties.shader = { name: material.type, - uniforms: UniformsUtils.clone( shader.uniforms ), + uniforms: cloneUniforms( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; @@ -22630,6 +24150,9 @@ material.onBeforeCompile( materialProperties.shader, _this ); + // Computing code again as onBeforeCompile may have changed the shaders + code = programCache.getProgramCode( material, parameters ); + program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); materialProperties.program = program; @@ -22686,14 +24209,26 @@ materialProperties.fog = fog; // store the light setup it was created for + if ( lightsHash === undefined ) { + + materialProperties.lightsHash = lightsHash = {}; + + } - materialProperties.lightsHash = lights.state.hash; + lightsHash.stateID = lightsStateHash.stateID; + lightsHash.directionalLength = lightsStateHash.directionalLength; + lightsHash.pointLength = lightsStateHash.pointLength; + lightsHash.spotLength = lightsStateHash.spotLength; + lightsHash.rectAreaLength = lightsStateHash.rectAreaLength; + lightsHash.hemiLength = lightsStateHash.hemiLength; + lightsHash.shadowsLength = lightsStateHash.shadowsLength; if ( material.lights ) { // 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.spotLights.value = lights.state.spot; uniforms.rectAreaLights.value = lights.state.rectArea; @@ -22720,11 +24255,14 @@ function setProgram( camera, fog, material, object ) { - _usedTextureUnits = 0; + textures.resetTextureUnits(); var materialProperties = properties.get( material ); var lights = currentRenderState.state.lights; + var lightsHash = materialProperties.lightsHash; + var lightsStateHash = lights.state.hash; + if ( _clippingEnabled ) { if ( _localClippingEnabled || camera !== _currentCamera ) { @@ -22754,7 +24292,13 @@ material.needsUpdate = true; - } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) { + } else if ( material.lights && ( lightsHash.stateID !== lightsStateHash.stateID || + lightsHash.directionalLength !== lightsStateHash.directionalLength || + lightsHash.pointLength !== lightsStateHash.pointLength || + lightsHash.spotLength !== lightsStateHash.spotLength || + lightsHash.rectAreaLength !== lightsStateHash.rectAreaLength || + lightsHash.hemiLength !== lightsStateHash.hemiLength || + lightsHash.shadowsLength !== lightsStateHash.shadowsLength ) ) { material.needsUpdate = true; @@ -22799,7 +24343,7 @@ } - if ( refreshProgram || camera !== _currentCamera ) { + if ( refreshProgram || _currentCamera !== camera ) { p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); @@ -22810,11 +24354,9 @@ } - // Avoid unneeded uniform updates per ArrayCamera's sub-camera - - if ( _currentCamera !== ( _currentArrayCamera || camera ) ) { + if ( _currentCamera !== camera ) { - _currentCamera = ( _currentArrayCamera || camera ); + _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when @@ -22900,7 +24442,7 @@ } - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture ); + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); } else { @@ -22978,6 +24520,12 @@ } + } else if ( material.isMeshMatcapMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + refreshUniformsMatcap( m_uniforms, material ); + } else if ( material.isMeshDepthMaterial ) { refreshUniformsCommon( m_uniforms, material ); @@ -23007,9 +24555,13 @@ refreshUniformsPoints( m_uniforms, material ); + } else if ( material.isSpriteMaterial ) { + + refreshUniformsSprites( m_uniforms, material ); + } else if ( material.isShadowMaterial ) { - m_uniforms.color.value = material.color; + m_uniforms.color.value.copy( material.color ); m_uniforms.opacity.value = material.opacity; } @@ -23020,17 +24572,23 @@ if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); } if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); material.uniformsNeedUpdate = false; } + if ( material.isSpriteMaterial ) { + + p_uniforms.setValue( _gl, 'center', object.center ); + + } + // common matrices p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); @@ -23049,7 +24607,7 @@ if ( material.color ) { - uniforms.diffuse.value = material.color; + uniforms.diffuse.value.copy( material.color ); } @@ -23085,7 +24643,7 @@ // WebGLRenderTargetCube will be flipped for backwards compatibility // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future - uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; + uniforms.flipEnvMap.value = material.envMap.isCubeTexture ? - 1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.refractionRatio.value = material.refractionRatio; @@ -23179,7 +24737,7 @@ function refreshUniformsLine( uniforms, material ) { - uniforms.diffuse.value = material.color; + uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; } @@ -23194,7 +24752,7 @@ function refreshUniformsPoints( uniforms, material ) { - uniforms.diffuse.value = material.color; + uniforms.diffuse.value.copy( material.color ); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * _pixelRatio; uniforms.scale.value = _height * 0.5; @@ -23215,9 +24773,30 @@ } + function refreshUniformsSprites( uniforms, material ) { + + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; + uniforms.map.value = material.map; + + if ( material.map !== null ) { + + if ( material.map.matrixAutoUpdate === true ) { + + material.map.updateMatrix(); + + } + + uniforms.uvTransform.value.copy( material.map.matrix ); + + } + + } + function refreshUniformsFog( uniforms, fog ) { - uniforms.fogColor.value = fog.color; + uniforms.fogColor.value.copy( fog.color ); if ( fog.isFog ) { @@ -23244,7 +24823,7 @@ function refreshUniformsPhong( uniforms, material ) { - uniforms.specular.value = material.specular; + uniforms.specular.value.copy( material.specular ); uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) if ( material.emissiveMap ) { @@ -23257,6 +24836,7 @@ uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } @@ -23264,6 +24844,7 @@ uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } @@ -23316,6 +24897,7 @@ uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } @@ -23323,6 +24905,7 @@ uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } @@ -23345,10 +24928,46 @@ function refreshUniformsPhysical( uniforms, material ) { + refreshUniformsStandard( uniforms, material ); + + uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + uniforms.clearCoat.value = material.clearCoat; uniforms.clearCoatRoughness.value = material.clearCoatRoughness; - refreshUniformsStandard( uniforms, material ); + } + + function refreshUniformsMatcap( uniforms, material ) { + + if ( material.matcap ) { + + uniforms.matcap.value = material.matcap; + + } + + if ( material.bumpMap ) { + + 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(); + + } + + if ( material.displacementMap ) { + + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; + + } } @@ -23386,6 +25005,7 @@ uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; + if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; } @@ -23393,6 +25013,7 @@ uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); + if ( material.side === BackSide ) uniforms.normalScale.value.negate(); } @@ -23411,6 +25032,7 @@ function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.pointLights.needsUpdate = value; @@ -23420,113 +25042,13 @@ } - // Textures - - function allocTextureUnit() { - - var textureUnit = _usedTextureUnits; - - if ( textureUnit >= capabilities.maxTextures ) { - - console.warn( 'THREE.WebGLRenderer: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); - - } - - _usedTextureUnits += 1; - - return textureUnit; - - } - - this.allocTextureUnit = allocTextureUnit; - - // this.setTexture2D = setTexture2D; - this.setTexture2D = ( function () { - - var warned = false; - - // backwards compatibility: peel texture.texture - return function setTexture2D( texture, slot ) { - - if ( texture && texture.isWebGLRenderTarget ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warned = true; - - } - - texture = texture.texture; - - } - - textures.setTexture2D( texture, slot ); - - }; - - }() ); - - this.setTexture = ( function () { - - var warned = false; - - return function setTexture( texture, slot ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); - warned = true; - - } - - textures.setTexture2D( texture, slot ); - - }; - - }() ); - - this.setTextureCube = ( function () { - - var warned = false; - - return function setTextureCube( texture, slot ) { - - // backwards compatibility: peel texture.texture - if ( texture && texture.isWebGLRenderTargetCube ) { - - if ( ! warned ) { - - console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warned = true; - - } - - texture = texture.texture; - - } - - // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - - // CompressedTexture can have Array in image :/ - - // this function alone should take care of cube textures - textures.setTextureCube( texture, slot ); - - } else { - - // assumed: texture property of THREE.WebGLRenderTargetCube - - textures.setTextureCubeDynamic( texture, slot ); + // - } + this.setFramebuffer = function ( value ) { - }; + _framebuffer = value; - }() ); + }; this.getRenderTarget = function () { @@ -23534,7 +25056,7 @@ }; - this.setRenderTarget = function ( renderTarget ) { + this.setRenderTarget = function ( renderTarget, activeCubeFace, activeMipMapLevel ) { _currentRenderTarget = renderTarget; @@ -23544,7 +25066,7 @@ } - var framebuffer = null; + var framebuffer = _framebuffer; var isCube = false; if ( renderTarget ) { @@ -23553,9 +25075,13 @@ if ( renderTarget.isWebGLRenderTargetCube ) { - framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; + framebuffer = __webglFramebuffer[ activeCubeFace || 0 ]; isCube = true; + } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + } else { framebuffer = __webglFramebuffer; @@ -23576,7 +25102,7 @@ if ( _currentFramebuffer !== framebuffer ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.bindFramebuffer( 36160, framebuffer ); _currentFramebuffer = framebuffer; } @@ -23588,7 +25114,7 @@ if ( isCube ) { var textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); + _gl.framebufferTexture2D( 36160, 36064, 34069 + ( activeCubeFace || 0 ), textureProperties.__webglTexture, activeMipMapLevel || 0 ); } @@ -23611,7 +25137,7 @@ if ( framebuffer !== _currentFramebuffer ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.bindFramebuffer( 36160, framebuffer ); restore = true; @@ -23623,23 +25149,23 @@ var textureFormat = texture.format; var textureType = texture.type; - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); return; } - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); return; } - if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) @@ -23659,7 +25185,7 @@ if ( restore ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + _gl.bindFramebuffer( 36160, _currentFramebuffer ); } @@ -23675,9 +25201,9 @@ var height = texture.image.height; var glFormat = utils.convert( texture.format ); - this.setTexture2D( texture, 0 ); + textures.setTexture2D( texture, 0 ); - _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 ); + _gl.copyTexImage2D( 3553, level || 0, glFormat, position.x, position.y, width, height, 0 ); }; @@ -23687,11 +25213,18 @@ var height = srcTexture.image.height; var glFormat = utils.convert( dstTexture.format ); var glType = utils.convert( dstTexture.type ); - var pixels = srcTexture.isDataTexture ? srcTexture.image.data : srcTexture.image; - this.setTexture2D( dstTexture, 0 ); + textures.setTexture2D( dstTexture, 0 ); + + if ( srcTexture.isDataTexture ) { + + _gl.texSubImage2D( 3553, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, pixels ); + } else { + + _gl.texSubImage2D( 3553, level || 0, position.x, position.y, glFormat, glType, srcTexture.image ); + + } }; @@ -23711,23 +25244,27 @@ } - FogExp2.prototype.isFogExp2 = true; + Object.assign( FogExp2.prototype, { - FogExp2.prototype.clone = function () { + isFogExp2: true, - return new FogExp2( this.color.getHex(), this.density ); + clone: function () { - }; + return new FogExp2( this.color, this.density ); - FogExp2.prototype.toJSON = function ( /* meta */ ) { + }, - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; + toJSON: function ( /* meta */ ) { - }; + return { + type: 'FogExp2', + color: this.color.getHex(), + density: this.density + }; + + } + + } ); /** * @author mrdoob / http://mrdoob.com/ @@ -23745,24 +25282,28 @@ } - Fog.prototype.isFog = true; + Object.assign( Fog.prototype, { - Fog.prototype.clone = function () { + isFog: true, - return new Fog( this.color.getHex(), this.near, this.far ); + clone: function () { - }; + return new Fog( this.color, this.near, this.far ); - Fog.prototype.toJSON = function ( /* meta */ ) { + }, - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; + toJSON: function ( /* meta */ ) { - }; + return { + type: 'Fog', + color: this.color.getHex(), + near: this.near, + far: this.far + }; + + } + + } ); /** * @author mrdoob / http://mrdoob.com/ @@ -23786,6 +25327,8 @@ constructor: Scene, + isScene: true, + copy: function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); @@ -23810,6 +25353,256 @@ return data; + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + + } ); + + /** + * @author benaadams / https://twitter.com/ben_a_adams + */ + + function InterleavedBuffer( array, stride ) { + + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; + + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; + + this.version = 0; + + } + + Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { + + set: function ( value ) { + + if ( value === true ) this.version ++; + + } + + } ); + + Object.assign( InterleavedBuffer.prototype, { + + isInterleavedBuffer: true, + + onUploadCallback: function () {}, + + setArray: function ( array ) { + + if ( Array.isArray( array ) ) { + + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + + } + + this.count = array !== undefined ? array.length / this.stride : 0; + this.array = array; + + return this; + + }, + + setDynamic: function ( value ) { + + this.dynamic = value; + + return this; + + }, + + copy: function ( source ) { + + this.array = new source.array.constructor( source.array ); + this.count = source.count; + this.stride = source.stride; + this.dynamic = source.dynamic; + + return this; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.stride; + index2 *= attribute.stride; + + for ( var i = 0, l = this.stride; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + return this; + + }, + + set: function ( value, offset ) { + + if ( offset === undefined ) offset = 0; + + this.array.set( value, offset ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + onUpload: function ( callback ) { + + this.onUploadCallback = callback; + + return this; + + } + + } ); + + /** + * @author benaadams / https://twitter.com/ben_a_adams + */ + + function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { + + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; + + this.normalized = normalized === true; + + } + + Object.defineProperties( InterleavedBufferAttribute.prototype, { + + count: { + + get: function () { + + return this.data.count; + + } + + }, + + array: { + + get: function () { + + return this.data.array; + + } + + } + + } ); + + Object.assign( InterleavedBufferAttribute.prototype, { + + isInterleavedBufferAttribute: true, + + setX: function ( index, x ) { + + this.data.array[ index * this.data.stride + this.offset ] = x; + + return this; + + }, + + setY: function ( index, y ) { + + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + + return this; + + }, + + setZ: function ( index, z ) { + + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + + return this; + + }, + + setW: function ( index, w ) { + + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + + return this; + + }, + + getX: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset ]; + + }, + + getY: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 1 ]; + + }, + + getZ: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 2 ]; + + }, + + getW: function ( index ) { + + return this.data.array[ index * this.data.stride + this.offset + 3 ]; + + }, + + setXY: function ( index, x, y ) { + + index = index * this.data.stride + this.offset; + + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + 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; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + 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; + this.data.array[ index + 3 ] = w; + + return this; + } } ); @@ -23819,11 +25612,9 @@ * * parameters = { * color: , - * opacity: , * map: new THREE.Texture( ), - * - * uvOffset: new THREE.Vector2(), - * uvScale: new THREE.Vector2() + * rotation: , + * sizeAttenuation: * } */ @@ -23838,8 +25629,10 @@ this.rotation = 0; - this.fog = false; + this.sizeAttenuation = true; + this.lights = false; + this.transparent = true; this.setValues( parameters ); @@ -23858,6 +25651,8 @@ this.rotation = source.rotation; + this.sizeAttenuation = source.sizeAttenuation; + return this; }; @@ -23867,12 +25662,34 @@ * @author alteredq / http://alteredqualia.com/ */ + var geometry; + function Sprite( material ) { Object3D.call( this ); this.type = 'Sprite'; + if ( geometry === undefined ) { + + geometry = new BufferGeometry(); + + var 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 + ] ); + + var interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); + + geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); + geometry.addAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); + geometry.addAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); + + } + + this.geometry = geometry; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); this.center = new Vector2( 0.5, 0.5 ); @@ -23888,18 +25705,90 @@ raycast: ( function () { var intersectPoint = new Vector3(); - var worldPosition = new Vector3(); var worldScale = new Vector3(); + var mvPosition = new Vector3(); + + var alignedPosition = new Vector2(); + var rotatedPosition = new Vector2(); + var viewWorldMatrix = new Matrix4(); + + var vA = new Vector3(); + var vB = new Vector3(); + var vC = new Vector3(); + + var uvA = new Vector2(); + var uvB = new Vector2(); + var uvC = new Vector2(); + + function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { + + // compute position in camera space + alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); + + // to check if rotation is not zero + if ( sin !== undefined ) { + + rotatedPosition.x = ( cos * alignedPosition.x ) - ( sin * alignedPosition.y ); + rotatedPosition.y = ( sin * alignedPosition.x ) + ( cos * alignedPosition.y ); + + } else { + + rotatedPosition.copy( alignedPosition ); + + } - return function raycast( raycaster, intersects ) { - worldPosition.setFromMatrixPosition( this.matrixWorld ); - raycaster.ray.closestPointToPoint( worldPosition, intersectPoint ); + vertexPosition.copy( mvPosition ); + vertexPosition.x += rotatedPosition.x; + vertexPosition.y += rotatedPosition.y; + + // transform to world space + vertexPosition.applyMatrix4( viewWorldMatrix ); + + } + + return function raycast( raycaster, intersects ) { worldScale.setFromMatrixScale( this.matrixWorld ); - var guessSizeSq = worldScale.x * worldScale.y / 4; + viewWorldMatrix.getInverse( this.modelViewMatrix ).premultiply( this.matrixWorld ); + mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + + var rotation = this.material.rotation; + var sin, cos; + if ( rotation !== 0 ) { + + cos = Math.cos( rotation ); + sin = Math.sin( rotation ); + + } + + var center = this.center; + + 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 ); + + uvA.set( 0, 0 ); + uvB.set( 1, 0 ); + uvC.set( 1, 1 ); + + // check first triangle + var intersect = raycaster.ray.intersectTriangle( vA, vB, vC, false, intersectPoint ); + + if ( intersect === null ) { + + // check second triangle + transformVertex( vB.set( - 0.5, 0.5, 0 ), mvPosition, center, worldScale, sin, cos ); + uvB.set( 0, 1 ); - if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return; + intersect = raycaster.ray.intersectTriangle( vA, vC, vB, false, intersectPoint ); + if ( intersect === null ) { + + return; + + } + + } var distance = raycaster.ray.origin.distanceTo( intersectPoint ); @@ -23909,6 +25798,7 @@ distance: distance, point: intersectPoint.clone(), + uv: Triangle.getUV( intersectPoint, vA, vB, vC, uvA, uvB, uvC, new Vector2() ), face: null, object: this @@ -23962,6 +25852,8 @@ constructor: LOD, + isLOD: true, + copy: function ( source ) { Object3D.prototype.copy.call( this, source, false ); @@ -24002,6 +25894,8 @@ this.add( object ); + return this; + }, getObjectForDistance: function ( distance ) { @@ -24108,6 +26002,120 @@ } ); + /** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + + function SkinnedMesh( geometry, material ) { + + if ( geometry && geometry.isGeometry ) { + + console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + + } + + Mesh.call( this, geometry, material ); + + this.type = 'SkinnedMesh'; + + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); + + } + + SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + + constructor: SkinnedMesh, + + isSkinnedMesh: true, + + bind: function ( skeleton, bindMatrix ) { + + this.skeleton = skeleton; + + if ( bindMatrix === undefined ) { + + this.updateMatrixWorld( true ); + + this.skeleton.calculateInverses(); + + bindMatrix = this.matrixWorld; + + } + + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); + + }, + + pose: function () { + + this.skeleton.pose(); + + }, + + normalizeSkinWeights: function () { + + var vector = new Vector4(); + + var skinWeight = this.geometry.attributes.skinWeight; + + for ( var i = 0, l = skinWeight.count; i < l; i ++ ) { + + vector.x = skinWeight.getX( i ); + vector.y = skinWeight.getY( i ); + vector.z = skinWeight.getZ( i ); + vector.w = skinWeight.getW( i ); + + var scale = 1.0 / vector.manhattanLength(); + + if ( scale !== Infinity ) { + + vector.multiplyScalar( scale ); + + } else { + + vector.set( 1, 0, 0, 0 ); // do something reasonable + + } + + skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); + + } + + }, + + updateMatrixWorld: function ( force ) { + + Mesh.prototype.updateMatrixWorld.call( this, force ); + + if ( this.bindMode === 'attached' ) { + + this.bindMatrixInverse.getInverse( this.matrixWorld ); + + } else if ( this.bindMode === 'detached' ) { + + this.bindMatrixInverse.getInverse( this.bindMatrix ); + + } else { + + console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); + + } + + }, + + clone: function () { + + return new this.constructor( this.geometry, this.material ).copy( this ); + + } + + } ); + /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ @@ -24305,329 +26313,127 @@ } ); /** - * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com + * + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round" + * } */ - function SkinnedMesh( geometry, material ) { + function LineBasicMaterial( parameters ) { - Mesh.call( this, geometry, material ); + Material.call( this ); - this.type = 'SkinnedMesh'; + this.type = 'LineBasicMaterial'; - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); + this.color = new Color( 0xffffff ); - var bones = this.initBones(); - var skeleton = new Skeleton( bones ); + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - this.bind( skeleton, this.matrixWorld ); + this.lights = false; - this.normalizeSkinWeights(); + this.setValues( parameters ); } - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + LineBasicMaterial.prototype = Object.create( Material.prototype ); + LineBasicMaterial.prototype.constructor = LineBasicMaterial; - constructor: SkinnedMesh, + LineBasicMaterial.prototype.isLineBasicMaterial = true; - isSkinnedMesh: true, + LineBasicMaterial.prototype.copy = function ( source ) { - initBones: function () { + Material.prototype.copy.call( this, source ); - var bones = [], bone, gbone; - var i, il; + this.color.copy( source.color ); - if ( this.geometry && this.geometry.bones !== undefined ) { + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; - // first, create array of 'Bone' objects from geometry data + return this; - for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { + }; - gbone = this.geometry.bones[ i ]; + /** + * @author mrdoob / http://mrdoob.com/ + */ - // create new 'Bone' object + function Line( geometry, material, mode ) { - bone = new Bone(); - bones.push( bone ); + if ( mode === 1 ) { - // apply values + console.error( 'THREE.Line: parameter THREE.LinePieces no longer supported. Use THREE.LineSegments instead.' ); - bone.name = gbone.name; - bone.position.fromArray( gbone.pos ); - bone.quaternion.fromArray( gbone.rotq ); - if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + } - } + Object3D.call( this ); - // second, create bone hierarchy + this.type = 'Line'; - for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) { + this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); + this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); - gbone = this.geometry.bones[ i ]; + } - if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) { + Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - // subsequent bones in the hierarchy + constructor: Line, - bones[ gbone.parent ].add( bones[ i ] ); + isLine: true, - } else { + computeLineDistances: ( function () { - // topmost bone, immediate child of the skinned mesh + var start = new Vector3(); + var end = new Vector3(); - this.add( bones[ i ] ); + return function computeLineDistances() { - } + var geometry = this.geometry; - } + if ( geometry.isBufferGeometry ) { - } + // we assume non-indexed geometry - // now the bones are part of the scene graph and children of the skinned mesh. - // let's update the corresponding matrices + if ( geometry.index === null ) { - this.updateMatrixWorld( true ); + var positionAttribute = geometry.attributes.position; + var lineDistances = [ 0 ]; - return bones; + for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { - }, + start.fromBufferAttribute( positionAttribute, i - 1 ); + end.fromBufferAttribute( positionAttribute, i ); - bind: function ( skeleton, bindMatrix ) { + lineDistances[ i ] = lineDistances[ i - 1 ]; + lineDistances[ i ] += start.distanceTo( end ); - this.skeleton = skeleton; + } - if ( bindMatrix === undefined ) { + geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - this.updateMatrixWorld( true ); + } else { - this.skeleton.calculateInverses(); + console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - bindMatrix = this.matrixWorld; + } - } + } else if ( geometry.isGeometry ) { - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); + var vertices = geometry.vertices; + var lineDistances = geometry.lineDistances; - }, + lineDistances[ 0 ] = 0; - pose: function () { - - this.skeleton.pose(); - - }, - - normalizeSkinWeights: function () { - - var scale, i; - - if ( this.geometry && this.geometry.isGeometry ) { - - for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) { - - var sw = this.geometry.skinWeights[ i ]; - - scale = 1.0 / sw.manhattanLength(); - - if ( scale !== Infinity ) { - - sw.multiplyScalar( scale ); - - } else { - - sw.set( 1, 0, 0, 0 ); // do something reasonable - - } - - } - - } else if ( this.geometry && this.geometry.isBufferGeometry ) { - - var vec = new Vector4(); - - var skinWeight = this.geometry.attributes.skinWeight; - - for ( i = 0; i < skinWeight.count; i ++ ) { - - vec.x = skinWeight.getX( i ); - vec.y = skinWeight.getY( i ); - vec.z = skinWeight.getZ( i ); - vec.w = skinWeight.getW( i ); - - scale = 1.0 / vec.manhattanLength(); - - if ( scale !== Infinity ) { - - vec.multiplyScalar( scale ); - - } else { - - vec.set( 1, 0, 0, 0 ); // do something reasonable - - } - - skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); - - } - - } - - }, - - updateMatrixWorld: function ( force ) { - - Mesh.prototype.updateMatrixWorld.call( this, force ); - - if ( this.bindMode === 'attached' ) { - - this.bindMatrixInverse.getInverse( this.matrixWorld ); - - } else if ( this.bindMode === 'detached' ) { - - this.bindMatrixInverse.getInverse( this.bindMatrix ); - - } else { - - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - - } - - }, - - clone: function () { - - return new this.constructor( this.geometry, this.material ).copy( this ); - - } - - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ - - function LineBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'LineBasicMaterial'; - - this.color = new Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.lights = false; - - this.setValues( parameters ); - - } - - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; - - LineBasicMaterial.prototype.isLineBasicMaterial = true; - - LineBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Line( geometry, material, mode ) { - - if ( mode === 1 ) { - - console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); - return new LineSegments( geometry, material ); - - } - - Object3D.call( this ); - - this.type = 'Line'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); - - } - - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Line, - - isLine: true, - - computeLineDistances: ( function () { - - var start = new Vector3(); - var end = new Vector3(); - - return function computeLineDistances() { - - var geometry = this.geometry; - - if ( geometry.isBufferGeometry ) { - - // we assume non-indexed geometry - - if ( geometry.index === null ) { - - var positionAttribute = geometry.attributes.position; - var lineDistances = [ 0 ]; - - for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { - - start.fromBufferAttribute( positionAttribute, i - 1 ); - end.fromBufferAttribute( positionAttribute, i ); - - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += start.distanceTo( end ); - - } - - geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - - } else { - - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - - } - - } else if ( geometry.isGeometry ) { - - var vertices = geometry.vertices; - var lineDistances = geometry.lineDistances; - - lineDistances[ 0 ] = 0; - - for ( var i = 1, l = vertices.length; i < l; i ++ ) { + for ( var i = 1, l = vertices.length; i < l; i ++ ) { lineDistances[ i ] = lineDistances[ i - 1 ]; lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); @@ -24651,7 +26457,6 @@ return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; - var precisionSq = precision * precision; var geometry = this.geometry; var matrixWorld = this.matrixWorld; @@ -24662,6 +26467,7 @@ sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); + sphere.radius += precision; if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; @@ -24670,6 +26476,9 @@ inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + var localPrecision = precision / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localPrecisionSq = localPrecision * localPrecision; + var vStart = new Vector3(); var vEnd = new Vector3(); var interSegment = new Vector3(); @@ -24696,7 +26505,7 @@ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - if ( distSq > precisionSq ) continue; + if ( distSq > localPrecisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation @@ -24728,7 +26537,7 @@ var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - if ( distSq > precisionSq ) continue; + if ( distSq > localPrecisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation @@ -24762,7 +26571,7 @@ var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - if ( distSq > precisionSq ) continue; + if ( distSq > localPrecisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation @@ -24909,6 +26718,8 @@ * * size: , * sizeAttenuation: + * + * morphTargets: * } */ @@ -24925,6 +26736,8 @@ this.size = 1; this.sizeAttenuation = true; + this.morphTargets = false; + this.lights = false; this.setValues( parameters ); @@ -24947,6 +26760,8 @@ this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; + this.morphTargets = source.morphTargets; + return this; }; @@ -25089,26 +26904,6 @@ } ); - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Group() { - - Object3D.call( this ); - - this.type = 'Group'; - - } - - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Group, - - isGroup: true - - } ); - /** * @author mrdoob / http://mrdoob.com/ */ @@ -25117,6 +26912,11 @@ Texture.call( this, 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; } @@ -25169,6 +26969,22 @@ CompressedTexture.prototype.isCompressedTexture = true; + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.needsUpdate = true; + + } + + CanvasTexture.prototype = Object.create( Texture.prototype ); + CanvasTexture.prototype.constructor = CanvasTexture; + CanvasTexture.prototype.isCanvasTexture = true; + /** * @author Matt DesLauriers / @mattdesl * @author atix / arthursilber.de @@ -25436,6 +27252,12 @@ var i, j; + if ( func.length < 3 ) { + + console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); + + } + // generate vertices, normals and uvs var sliceCount = slices + 1; @@ -26323,6 +28145,16 @@ TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; + TubeBufferGeometry.prototype.toJSON = function () { + + var data = BufferGeometry.prototype.toJSON.call( this ); + + data.path = this.parameters.path.toJSON(); + + return data; + + }; + /** * @author oosmoxiecode * @author Mugen87 / https://github.com/Mugen87 @@ -26814,7 +28646,7 @@ ear = cureLocalIntersections( ear, triangles, dim ); earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); - // as a last resort, try splitting the remaining polygon into two + // as a last resort, try splitting the remaining polygon into two } else if ( pass === 2 ) { @@ -27556,11 +29388,12 @@ * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * amount: , // Depth to extrude the shape + * 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 is bevel + * 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 @@ -27591,6 +29424,17 @@ ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; + ExtrudeGeometry.prototype.toJSON = function () { + + var data = Geometry.prototype.toJSON.call( this ); + + var shapes = this.parameters.shapes; + var options = this.parameters.options; + + return toJSON( shapes, options, data ); + + }; + // ExtrudeBufferGeometry function ExtrudeBufferGeometry( shapes, options ) { @@ -27614,7 +29458,7 @@ for ( var i = 0, l = shapes.length; i < l; i ++ ) { var shape = shapes[ i ]; - addShape( shape, options ); + addShape( shape ); } @@ -27635,17 +29479,27 @@ var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; var steps = options.steps !== undefined ? options.steps : 1; - var amount = options.amount !== undefined ? options.amount : 100; + var depth = options.depth !== undefined ? options.depth : 100; var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; + var bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; var extrudePath = options.extrudePath; var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; + // deprecated options + + if ( options.amount !== undefined ) { + + console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); + depth = options.amount; + + } + // var extrudePts, extrudeByPath = false; @@ -27679,6 +29533,7 @@ bevelSegments = 0; bevelThickness = 0; bevelSize = 0; + bevelOffset = 0; } @@ -27915,7 +29770,7 @@ t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); + bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape @@ -27946,7 +29801,7 @@ } - bs = bevelSize; + bs = bevelSize + bevelOffset; // Back facing vertices @@ -27986,7 +29841,7 @@ if ( ! extrudeByPath ) { - v( vert.x, vert.y, amount / steps * s ); + v( vert.x, vert.y, depth / steps * s ); } else { @@ -28013,14 +29868,14 @@ t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); + bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, amount + z ); + v( vert.x, vert.y, depth + z ); } @@ -28037,7 +29892,7 @@ if ( ! extrudeByPath ) { - v( vert.x, vert.y, amount + z ); + v( vert.x, vert.y, depth + z ); } else { @@ -28250,6 +30105,19 @@ ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; + ExtrudeBufferGeometry.prototype.toJSON = function () { + + var data = BufferGeometry.prototype.toJSON.call( this ); + + var shapes = this.parameters.shapes; + var options = this.parameters.options; + + return toJSON( shapes, options, data ); + + }; + + // + var WorldUVGenerator = { generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { @@ -28307,6 +30175,36 @@ } }; + function toJSON( shapes, options, data ) { + + // + + data.shapes = []; + + if ( Array.isArray( shapes ) ) { + + for ( var i = 0, l = shapes.length; i < l; i ++ ) { + + var shape = shapes[ i ]; + + data.shapes.push( shape.uuid ); + + } + + } else { + + data.shapes.push( shapes.uuid ); + + } + + // + + if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); + + return data; + + } + /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ @@ -28322,7 +30220,8 @@ * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into text bevel goes - * bevelSize: // how far from text outline is bevel + * bevelSize: , // how far from text outline (including bevelOffset) is bevel + * bevelOffset: // how far from text outline does bevel start * } */ @@ -28362,11 +30261,11 @@ } - var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); + var shapes = font.generateShapes( text, parameters.size ); // translate parameters to ExtrudeGeometry API - parameters.amount = parameters.height !== undefined ? parameters.height : 50; + parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults @@ -28469,6 +30368,10 @@ var v = iy / heightSegments; + // special case for the poles + + var uOffset = ( iy == 0 ) ? 0.5 / widthSegments : ( ( iy == heightSegments ) ? - 0.5 / widthSegments : 0 ); + for ( ix = 0; ix <= widthSegments; ix ++ ) { var u = ix / widthSegments; @@ -28483,12 +30386,12 @@ // normal - normal.set( vertex.x, vertex.y, vertex.z ).normalize(); + normal.copy( vertex ).normalize(); normals.push( normal.x, normal.y, normal.z ); // uv - uvs.push( u, 1 - v ); + uvs.push( u + uOffset, 1 - v ); verticesRow.push( index ++ ); @@ -28673,7 +30576,6 @@ RingBufferGeometry.prototype.constructor = RingBufferGeometry; /** - * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 * @author bhouston / http://clara.io * @author Mugen87 / https://github.com/Mugen87 @@ -28890,7 +30792,7 @@ var shapes = this.parameters.shapes; - return toJSON( shapes, data ); + return toJSON$1( shapes, data ); }; @@ -28968,17 +30870,15 @@ shapeVertices = shapeVertices.reverse(); - // also check if holes are in the opposite direction - - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { + } - shapeHole = shapeHoles[ i ]; + for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + shapeHole = shapeHoles[ i ]; - shapeHoles[ i ] = shapeHole.reverse(); + if ( ShapeUtils.isClockWise( shapeHole ) === true ) { - } + shapeHoles[ i ] = shapeHole.reverse(); } @@ -29035,13 +30935,13 @@ var shapes = this.parameters.shapes; - return toJSON( shapes, data ); + return toJSON$1( shapes, data ); }; // - function toJSON( shapes, data ) { + function toJSON$1( shapes, data ) { data.shapes = []; @@ -29644,7 +31544,7 @@ - var Geometries = Object.freeze({ + var Geometries = /*#__PURE__*/Object.freeze({ WireframeGeometry: WireframeGeometry, ParametricGeometry: ParametricGeometry, ParametricBufferGeometry: ParametricBufferGeometry, @@ -29767,6 +31667,7 @@ * bumpScale: , * * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), @@ -29821,6 +31722,7 @@ this.bumpScale = 1; this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; @@ -29882,6 +31784,7 @@ this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; @@ -29917,6 +31820,8 @@ * * parameters = { * reflectivity: + * clearCoat: + * clearCoatRoughness: * } */ @@ -29983,6 +31888,7 @@ * bumpScale: , * * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), @@ -30033,6 +31939,7 @@ this.bumpScale = 1; this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; @@ -30090,6 +31997,7 @@ this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; @@ -30166,6 +32074,7 @@ * bumpScale: , * * normalMap: new THREE.Texture( ), + * normalMapType: THREE.TangentSpaceNormalMap, * normalScale: , * * displacementMap: new THREE.Texture( ), @@ -30191,6 +32100,7 @@ this.bumpScale = 1; this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; @@ -30224,6 +32134,7 @@ this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; @@ -30366,6 +32277,111 @@ }; + /** + * @author WestLangley / http://github.com/WestLangley + * + * 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: + * } + */ + + function MeshMatcapMaterial( parameters ) { + + Material.call( this ); + + 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; + + this.alphaMap = null; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.lights = false; + + this.setValues( parameters ); + + } + + MeshMatcapMaterial.prototype = Object.create( Material.prototype ); + MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; + + MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; + + MeshMatcapMaterial.prototype.copy = function ( source ) { + + Material.prototype.copy.call( this, 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.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; + + return this; + + }; + /** * @author alteredq / http://alteredqualia.com/ * @@ -30414,7 +32430,7 @@ - var Materials = Object.freeze({ + var Materials = /*#__PURE__*/Object.freeze({ ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, @@ -30429,2622 +32445,2618 @@ MeshDepthMaterial: MeshDepthMaterial, MeshDistanceMaterial: MeshDistanceMaterial, MeshBasicMaterial: MeshBasicMaterial, + MeshMatcapMaterial: MeshMatcapMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); /** - * @author mrdoob / http://mrdoob.com/ + * @author tschw + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ */ - var Cache = { - - enabled: false, + var AnimationUtils = { - files: {}, + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function ( array, from, to ) { - add: function ( key, file ) { + if ( AnimationUtils.isTypedArray( array ) ) { - if ( this.enabled === false ) return; + // 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 ) ); - // console.log( 'THREE.Cache', 'Adding key:', key ); + } - this.files[ key ] = file; + return array.slice( from, to ); }, - get: function ( key ) { + // converts an array to a specific type + convertArray: function ( array, type, forceClone ) { - if ( this.enabled === false ) return; + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - // console.log( 'THREE.Cache', 'Checking key:', key ); + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - return this.files[ key ]; + return new type( array ); // create typed array + + } + + return Array.prototype.slice.call( array ); // create Array }, - remove: function ( key ) { + isTypedArray: function ( object ) { - delete this.files[ key ]; + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); }, - clear: function () { + // returns an array by which times and values can be sorted + getKeyframeOrder: function ( times ) { - this.files = {}; + function compareTime( i, j ) { - } + return times[ i ] - times[ j ]; - }; + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + var n = times.length; + var result = new Array( n ); + for ( var i = 0; i !== n; ++ i ) result[ i ] = i; - function LoadingManager( onLoad, onProgress, onError ) { + result.sort( compareTime ); - var scope = this; + return result; - var isLoading = false; - var itemsLoaded = 0; - var itemsTotal = 0; - var urlModifier = undefined; + }, - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function ( values, stride, order ) { - this.itemStart = function ( url ) { + var nValues = values.length; + var result = new values.constructor( nValues ); - itemsTotal ++; + for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - if ( isLoading === false ) { + var srcOffset = order[ i ] * stride; - if ( scope.onStart !== undefined ) { + for ( var j = 0; j !== stride; ++ j ) { - scope.onStart( url, itemsLoaded, itemsTotal ); + result[ dstOffset ++ ] = values[ srcOffset + j ]; } } - isLoading = true; + return result; - }; + }, - this.itemEnd = function ( url ) { + // function for parsing AOS keyframe formats + flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { - itemsLoaded ++; + var i = 1, key = jsonKeys[ 0 ]; - if ( scope.onProgress !== undefined ) { + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - scope.onProgress( url, itemsLoaded, itemsTotal ); + key = jsonKeys[ i ++ ]; } - if ( itemsLoaded === itemsTotal ) { + if ( key === undefined ) return; // no data - isLoading = false; + var value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - if ( scope.onLoad !== undefined ) { + if ( Array.isArray( value ) ) { - scope.onLoad(); + do { - } + value = key[ valuePropertyName ]; - } + if ( value !== undefined ) { - }; + times.push( key.time ); + values.push.apply( values, value ); // push all elements - this.itemError = function ( url ) { + } - if ( scope.onError !== undefined ) { + key = jsonKeys[ i ++ ]; - scope.onError( url ); + } while ( key !== undefined ); - } + } else if ( value.toArray !== undefined ) { - }; + // ...assume THREE.Math-ish - this.resolveURL = function ( url ) { + do { - if ( urlModifier ) { + value = key[ valuePropertyName ]; - return urlModifier( url ); + if ( value !== undefined ) { - } + times.push( key.time ); + value.toArray( values, values.length ); - return url; + } - }; + key = jsonKeys[ i ++ ]; - this.setURLModifier = function ( transform ) { + } while ( key !== undefined ); - urlModifier = transform; - return this; + } else { - }; + // otherwise push as-is - } + do { - var DefaultLoadingManager = new LoadingManager(); + value = key[ valuePropertyName ]; - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( value !== undefined ) { - var loading = {}; + times.push( key.time ); + values.push( value ); - function FileLoader( manager ) { + } - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + key = jsonKeys[ i ++ ]; - } + } while ( key !== undefined ); - Object.assign( FileLoader.prototype, { + } - load: function ( url, onLoad, onProgress, onError ) { + } - if ( url === undefined ) url = ''; + }; - if ( this.path !== undefined ) url = this.path + url; + /** + * 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 + * + * @author tschw + */ - url = this.manager.resolveURL( url ); + function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - var scope = this; + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - var cached = Cache.get( url ); + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - if ( cached !== undefined ) { + } - scope.manager.itemStart( url ); + Object.assign( Interpolant.prototype, { - setTimeout( function () { + evaluate: function ( t ) { - if ( onLoad ) onLoad( cached ); + var pp = this.parameterPositions, + i1 = this._cachedIndex, - scope.manager.itemEnd( url ); + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - }, 0 ); + validate_interval: { - return cached; + seek: { - } + var right; - // Check if request is duplicate + linear_scan: { - if ( loading[ url ] !== undefined ) { + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - loading[ url ].push( { + for ( var giveUpAt = i1 + 2; ; ) { - onLoad: onLoad, - onProgress: onProgress, - onError: onError + if ( t1 === undefined ) { - } ); + if ( t < t0 ) break forward_scan; - return; + // after end - } + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); - // Check for data: URI - var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - var dataUriRegexResult = url.match( dataUriRegex ); + } - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { + if ( i1 === giveUpAt ) break; // this loop - var mimeType = dataUriRegexResult[ 1 ]; - var isBase64 = !! dataUriRegexResult[ 2 ]; - var data = dataUriRegexResult[ 3 ]; + t0 = t1; + t1 = pp[ ++ i1 ]; - data = window.decodeURIComponent( data ); + if ( t < t1 ) { - if ( isBase64 ) data = window.atob( data ); + // we have arrived at the sought interval + break seek; - try { + } - var response; - var responseType = ( this.responseType || '' ).toLowerCase(); + } - switch ( responseType ) { + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - case 'arraybuffer': - case 'blob': + } - var view = new Uint8Array( data.length ); + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - for ( var i = 0; i < data.length; i ++ ) { + // looping? - view[ i ] = data.charCodeAt( i ); + var t1global = pp[ 1 ]; - } + if ( t < t1global ) { - if ( responseType === 'blob' ) { + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - response = new Blob( [ view.buffer ], { type: mimeType } ); + } - } else { + // linear reverse scan - response = view.buffer; + for ( var giveUpAt = i1 - 2; ; ) { - } + if ( t0 === undefined ) { - break; + // before start - case 'document': + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - var parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); + } - break; + if ( i1 === giveUpAt ) break; // this loop - case 'json': + t1 = t0; + t0 = pp[ -- i1 - 1 ]; - response = JSON.parse( data ); + if ( t >= t0 ) { - break; + // we have arrived at the sought interval + break seek; - default: // 'text' or other + } - response = data; + } - break; + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - } + } - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - window.setTimeout( function () { + // the interval is valid - if ( onLoad ) onLoad( response ); + break validate_interval; - scope.manager.itemEnd( url ); + } // linear scan - }, 0 ); + // binary search - } catch ( error ) { + while ( i1 < right ) { - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - window.setTimeout( function () { + var mid = ( i1 + right ) >>> 1; - if ( onError ) onError( error ); + if ( t < pp[ mid ] ) { - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + right = mid; - }, 0 ); + } else { - } + i1 = mid + 1; - } else { + } - // Initialise array for duplicate requests + } - loading[ url ] = []; + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - loading[ url ].push( { + // check boundary cases, again - onLoad: onLoad, - onProgress: onProgress, - onError: onError + if ( t0 === undefined ) { - } ); + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - var request = new XMLHttpRequest(); + } - request.open( 'GET', url, true ); + if ( t1 === undefined ) { - request.addEventListener( 'load', function ( event ) { + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); - var response = this.response; + } - Cache.add( url, response ); + } // seek - var callbacks = loading[ url ]; + this._cachedIndex = i1; - delete loading[ url ]; + this.intervalChanged_( i1, t0, t1 ); - if ( this.status === 200 ) { + } // validate_interval - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + return this.interpolate_( i1, t0, t, t1 ); - var callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); + }, - } + settings: null, // optional, subclass-specific settings structure + // Note: The indirection allows central control of many interpolants. - scope.manager.itemEnd( url ); + // --- Protected interface - } else if ( this.status === 0 ) { + DefaultSettings_: {}, - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - - var callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); - - } - - scope.manager.itemEnd( url ); - - } else { - - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + getSettings_: function () { - var callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + return this.settings || this.DefaultSettings_; - } + }, - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + copySampleValue_: function ( index ) { - } + // copies a sample value to the result buffer - }, false ); + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - request.addEventListener( 'progress', function ( event ) { + for ( var i = 0; i !== stride; ++ i ) { - var callbacks = loading[ url ]; + result[ i ] = values[ offset + i ]; - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + } - var callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); + return result; - } + }, - }, false ); + // Template methods for derived classes: - request.addEventListener( 'error', function ( event ) { + interpolate_: function ( /* i1, t0, t, t1 */ ) { - var callbacks = loading[ url ]; + throw new Error( 'call to abstract method' ); + // implementations shall return this.resultBuffer - delete loading[ url ]; + }, - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + intervalChanged_: function ( /* i1, t0, t1 */ ) { - var callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + // empty - } + } - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + } ); - }, false ); + //!\ DECLARE ALIAS AFTER assign prototype ! + Object.assign( Interpolant.prototype, { - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + //( 0, t, t0 ), returns this.resultBuffer + beforeStart_: Interpolant.prototype.copySampleValue_, - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); + //( N-1, tN-1, t ), returns this.resultBuffer + afterEnd_: Interpolant.prototype.copySampleValue_, - for ( var header in this.requestHeader ) { + } ); - request.setRequestHeader( header, this.requestHeader[ header ] ); + /** + * 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. + * + * @author tschw + */ - } + function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - request.send( null ); + Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - } + this._weightPrev = - 0; + this._offsetPrev = - 0; + this._weightNext = - 0; + this._offsetNext = - 0; - scope.manager.itemStart( url ); + } - return request; + CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - }, + constructor: CubicInterpolant, - setPath: function ( value ) { + DefaultSettings_: { - this.path = value; - return this; + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding }, - setResponseType: function ( value ) { + intervalChanged_: function ( i1, t0, t1 ) { - this.responseType = value; - return this; + var pp = this.parameterPositions, + iPrev = i1 - 2, + iNext = i1 + 1, - }, + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; - setWithCredentials: function ( value ) { + if ( tPrev === undefined ) { - this.withCredentials = value; - return this; + switch ( this.getSettings_().endingStart ) { - }, + case ZeroSlopeEnding: - setMimeType: function ( value ) { + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; - this.mimeType = value; - return this; + break; - }, + case WrapAroundEnding: - setRequestHeader: function ( value ) { + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - this.requestHeader = value; - return this; + break; - } + default: // ZeroCurvatureEnding - } ); + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; - /** - * @author mrdoob / http://mrdoob.com/ - * - * Abstract Base class to block based textures loader (dds, pvr, ...) - */ + } - function CompressedTextureLoader( manager ) { + } - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + if ( tNext === undefined ) { - // override in sub classes - this._parser = null; + switch ( this.getSettings_().endingEnd ) { - } + case ZeroSlopeEnding: - Object.assign( CompressedTextureLoader.prototype, { + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; - load: function ( url, onLoad, onProgress, onError ) { + break; - var scope = this; + case WrapAroundEnding: - var images = []; + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; - var texture = new CompressedTexture(); - texture.image = images; + break; - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); + default: // ZeroCurvatureEnding - function loadTexture( i ) { + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; - loader.load( url[ i ], function ( buffer ) { + } - var texDatas = scope._parser( buffer, true ); + } - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; + var halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; - loaded += 1; + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; - if ( loaded === 6 ) { + }, - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = LinearFilter; + interpolate_: function ( i1, t0, t, t1 ) { - texture.format = texDatas.format; - texture.needsUpdate = true; + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - if ( onLoad ) onLoad( texture ); + 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; - }, onProgress, onError ); + // evaluate polynomials - } + var sP = - wP * ppp + 2 * wP * pp - wP * p; + var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; + var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + var sN = wN * ppp - wN * pp; - if ( Array.isArray( url ) ) { + // combine data linearly - var loaded = 0; + for ( var i = 0; i !== stride; ++ i ) { - for ( var i = 0, il = url.length; i < il; ++ i ) { + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; - loadTexture( i ); + } - } + return result; - } else { + } - // compressed cubemap texture stored in a single DDS file + } ); - loader.load( url, function ( buffer ) { + /** + * @author tschw + */ - var texDatas = scope._parser( buffer, true ); + function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - if ( texDatas.isCubemap ) { + Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + } - for ( var f = 0; f < faces; f ++ ) { + LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - images[ f ] = { mipmaps: [] }; + constructor: LinearInterpolant, - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + interpolate_: function ( i1, t0, t, t1 ) { - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - } + offset1 = i1 * stride, + offset0 = offset1 - stride, - } + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - } else { + for ( var i = 0; i !== stride; ++ i ) { - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - } + } - if ( texDatas.mipmapCount === 1 ) { + return result; - texture.minFilter = LinearFilter; + } - } + } ); - texture.format = texDatas.format; - texture.needsUpdate = true; + /** + * + * Interpolant that evaluates to the sample value at the position preceeding + * the parameter. + * + * @author tschw + */ - if ( onLoad ) onLoad( texture ); + function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - }, onProgress, onError ); + Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - } + } - return texture; + DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - }, + constructor: DiscreteInterpolant, - setPath: function ( value ) { + interpolate_: function ( i1 /*, t0, t, t1 */ ) { - this.path = value; - return this; + return this.copySampleValue_( i1 - 1 ); } } ); /** - * @author Nikos M. / https://github.com/foo123/ * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + * A timed sequence of keyframes for a specific property. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw */ - function DataTextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + function KeyframeTrack( name, times, values, interpolation ) { - // override in sub classes - this._parser = null; + 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; - Object.assign( DataTextureLoader.prototype, { + this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); - load: function ( url, onLoad, onProgress, onError ) { + this.setInterpolation( interpolation || this.DefaultInterpolation ); - var scope = this; + } - var texture = new DataTexture(); + // Static methods - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); + Object.assign( KeyframeTrack, { - loader.load( url, function ( buffer ) { + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): - var texData = scope._parser( buffer ); + toJSON: function ( track ) { - if ( ! texData ) return; + var trackType = track.constructor; - if ( undefined !== texData.image ) { + var json; - texture.image = texData.image; + // derived classes can define a static toJSON method + if ( trackType.toJSON !== undefined ) { - } else if ( undefined !== texData.data ) { + json = trackType.toJSON( track ); - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; + } else { - } + // by default, we assume the data can be serialized as-is + json = { - texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; + 'name': track.name, + 'times': AnimationUtils.convertArray( track.times, Array ), + 'values': AnimationUtils.convertArray( track.values, Array ) - texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; - texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; + }; - texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + var interpolation = track.getInterpolation(); - if ( undefined !== texData.format ) { + if ( interpolation !== track.DefaultInterpolation ) { - texture.format = texData.format; + json.interpolation = interpolation; } - if ( undefined !== texData.type ) { - texture.type = texData.type; + } - } + json.type = track.ValueTypeName; // mandatory - if ( undefined !== texData.mipmaps ) { + return json; - texture.mipmaps = texData.mipmaps; + } - } + } ); - if ( 1 === texData.mipmapCount ) { + Object.assign( KeyframeTrack.prototype, { - texture.minFilter = LinearFilter; + constructor: KeyframeTrack, - } + TimeBufferType: Float32Array, - texture.needsUpdate = true; + ValueBufferType: Float32Array, - if ( onLoad ) onLoad( texture, texData ); + DefaultInterpolation: InterpolateLinear, - }, onProgress, onError ); + InterpolantFactoryMethodDiscrete: function ( result ) { + return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - return texture; + }, - } + InterpolantFactoryMethodLinear: function ( result ) { - } ); + return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + }, - function ImageLoader( manager ) { + InterpolantFactoryMethodSmooth: function ( result ) { - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - } + }, - Object.assign( ImageLoader.prototype, { + setInterpolation: function ( interpolation ) { - crossOrigin: 'Anonymous', + var factoryMethod; - load: function ( url, onLoad, onProgress, onError ) { + switch ( interpolation ) { - if ( url === undefined ) url = ''; + case InterpolateDiscrete: - if ( this.path !== undefined ) url = this.path + url; + factoryMethod = this.InterpolantFactoryMethodDiscrete; - url = this.manager.resolveURL( url ); + break; - var scope = this; + case InterpolateLinear: - var cached = Cache.get( url ); + factoryMethod = this.InterpolantFactoryMethodLinear; - if ( cached !== undefined ) { + break; - scope.manager.itemStart( url ); + case InterpolateSmooth: - setTimeout( function () { + factoryMethod = this.InterpolantFactoryMethodSmooth; - if ( onLoad ) onLoad( cached ); + break; - scope.manager.itemEnd( url ); + } - }, 0 ); + if ( factoryMethod === undefined ) { - return cached; + var message = "unsupported interpolation for " + + this.ValueTypeName + " keyframe track named " + this.name; - } + if ( this.createInterpolant === undefined ) { - var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { - image.addEventListener( 'load', function () { + this.setInterpolation( this.DefaultInterpolation ); - Cache.add( url, this ); + } else { - if ( onLoad ) onLoad( this ); + throw new Error( message ); // fatal, in this case - scope.manager.itemEnd( url ); + } - }, false ); + } - /* - image.addEventListener( 'progress', function ( event ) { + console.warn( 'THREE.KeyframeTrack:', message ); + return this; - if ( onProgress ) onProgress( event ); + } - }, false ); - */ + this.createInterpolant = factoryMethod; - image.addEventListener( 'error', function ( event ) { + return this; - if ( onError ) onError( event ); + }, - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + getInterpolation: function () { - }, false ); + switch ( this.createInterpolant ) { - if ( url.substr( 0, 5 ) !== 'data:' ) { + case this.InterpolantFactoryMethodDiscrete: - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + return InterpolateDiscrete; - } + case this.InterpolantFactoryMethodLinear: - scope.manager.itemStart( url ); + return InterpolateLinear; - image.src = url; + case this.InterpolantFactoryMethodSmooth: - return image; + return InterpolateSmooth; + + } }, - setCrossOrigin: function ( value ) { + getValueSize: function () { - this.crossOrigin = value; - return this; + return this.values.length / this.times.length; }, - setPath: function ( value ) { + // move all keyframes either forwards or backwards in time + shift: function ( timeOffset ) { - this.path = value; - return this; + if ( timeOffset !== 0.0 ) { - } + var times = this.times; - } ); + for ( var i = 0, n = times.length; i !== n; ++ i ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + times[ i ] += timeOffset; - function CubeTextureLoader( manager ) { + } - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + } - } + return this; - Object.assign( CubeTextureLoader.prototype, { + }, - crossOrigin: 'Anonymous', + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function ( timeScale ) { - load: function ( urls, onLoad, onProgress, onError ) { + if ( timeScale !== 1.0 ) { - var texture = new CubeTexture(); + var times = this.times; - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + for ( var i = 0, n = times.length; i !== n; ++ i ) { - var loaded = 0; + times[ i ] *= timeScale; - function loadTexture( i ) { - - loader.load( urls[ i ], function ( image ) { + } - texture.images[ i ] = image; + } - loaded ++; + return this; - if ( loaded === 6 ) { + }, - texture.needsUpdate = true; + // 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: function ( startTime, endTime ) { - if ( onLoad ) onLoad( texture ); + var times = this.times, + nKeys = times.length, + from = 0, + to = nKeys - 1; - } + while ( from !== nKeys && times[ from ] < startTime ) { - }, undefined, onError ); + ++ from; } - for ( var i = 0; i < urls.length; ++ i ) { + while ( to !== - 1 && times[ to ] > endTime ) { - loadTexture( i ); + -- to; } - return texture; - - }, + ++ to; // inclusive -> exclusive bound - setCrossOrigin: function ( value ) { + if ( from !== 0 || to !== nKeys ) { - this.crossOrigin = value; - return this; + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; - }, + var stride = this.getValueSize(); + this.times = AnimationUtils.arraySlice( times, from, to ); + this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); - setPath: function ( value ) { + } - this.path = value; return this; - } - - } ); + }, - /** - * @author mrdoob / http://mrdoob.com/ - */ + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate: function () { - function TextureLoader( manager ) { + var valid = true; - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + var valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - } + console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); + valid = false; - Object.assign( TextureLoader.prototype, { + } - crossOrigin: 'Anonymous', + var times = this.times, + values = this.values, - load: function ( url, onLoad, onProgress, onError ) { + nKeys = times.length; - var texture = new Texture(); + if ( nKeys === 0 ) { - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + console.error( 'THREE.KeyframeTrack: Track is empty.', this ); + valid = false; - loader.load( url, function ( image ) { + } - texture.image = image; + var prevTime = null; - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; + for ( var i = 0; i !== nKeys; i ++ ) { - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; + var currTime = times[ i ]; - if ( onLoad !== undefined ) { + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - onLoad( texture ); + console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); + valid = false; + break; } - }, onProgress, onError ); + if ( prevTime !== null && prevTime > currTime ) { - return texture; + console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); + valid = false; + break; - }, + } - setCrossOrigin: function ( value ) { + prevTime = currTime; - this.crossOrigin = value; - return this; + } - }, + if ( values !== undefined ) { - setPath: function ( value ) { + if ( AnimationUtils.isTypedArray( values ) ) { - this.path = value; - return this; + for ( var i = 0, n = values.length; i !== n; ++ i ) { - } + var value = values[ i ]; - } ); + if ( isNaN( value ) ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Extensible curve object - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u ) - * .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. - * - **/ + console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); + valid = false; + break; - /************************************************************** - * Abstract Curve base class - **************************************************************/ + } - function Curve() { + } - this.type = 'Curve'; + } - this.arcLengthDivisions = 200; + } - } + return valid; - Object.assign( Curve.prototype, { + }, - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] + // 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: function () { - getPoint: function ( /* t, optionalTarget */ ) { + var times = this.times, + values = this.values, + stride = this.getValueSize(), - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - }, + writeIndex = 1, + lastIndex = times.length - 1; - // Get point at relative position in curve according to arc length - // - u [0 .. 1] + for ( var i = 1; i < lastIndex; ++ i ) { - getPointAt: function ( u, optionalTarget ) { + var keep = false; - var t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); + var time = times[ i ]; + var timeNext = times[ i + 1 ]; - }, + // remove adjacent keyframes scheduled at the same time - // Get sequence of points using getPoint( t ) + if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - getPoints: function ( divisions ) { + if ( ! smoothInterpolation ) { - if ( divisions === undefined ) divisions = 5; + // remove unnecessary keyframes same as their neighbors - var points = []; + var offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - for ( var d = 0; d <= divisions; d ++ ) { + for ( var j = 0; j !== stride; ++ j ) { - points.push( this.getPoint( d / divisions ) ); + var value = values[ offset + j ]; - } + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - return points; + keep = true; + break; - }, + } - // Get sequence of points using getPointAt( u ) + } - getSpacedPoints: function ( divisions ) { + } else { - if ( divisions === undefined ) divisions = 5; + keep = true; - var points = []; + } - for ( var d = 0; d <= divisions; d ++ ) { + } - points.push( this.getPointAt( d / divisions ) ); + // in-place compaction - } + if ( keep ) { - return points; + if ( i !== writeIndex ) { - }, + times[ writeIndex ] = times[ i ]; - // Get total curve arc length + var readOffset = i * stride, + writeOffset = writeIndex * stride; - getLength: function () { + for ( var j = 0; j !== stride; ++ j ) { - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + values[ writeOffset + j ] = values[ readOffset + j ]; - }, + } - // Get list of cumulative segment lengths + } - getLengths: function ( divisions ) { + ++ writeIndex; - if ( divisions === undefined ) divisions = this.arcLengthDivisions; + } - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { + } - return this.cacheArcLengths; + // flush last keyframe (compaction looks ahead) - } + if ( lastIndex > 0 ) { - this.needsUpdate = false; + times[ writeIndex ] = times[ lastIndex ]; - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; + for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - cache.push( 0 ); + values[ writeOffset + j ] = values[ readOffset + j ]; - for ( p = 1; p <= divisions; p ++ ) { + } - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + ++ writeIndex; } - this.cacheArcLengths = cache; - - return cache; // { sums: cache, sum: sum }; Sum is in the last element. + if ( writeIndex !== times.length ) { - }, + this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - updateArcLengths: function () { + } - this.needsUpdate = true; - this.getLengths(); + return this; }, - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - - getUtoTmapping: function ( u, distance ) { - - var arcLengths = this.getLengths(); - - var i = 0, il = arcLengths.length; + clone: function () { - var targetArcLength; // The targeted u distance value to get + var times = AnimationUtils.arraySlice( this.times, 0 ); + var values = AnimationUtils.arraySlice( this.values, 0 ); - if ( distance ) { + var TypedKeyframeTrack = this.constructor; + var track = new TypedKeyframeTrack( this.name, times, values ); - targetArcLength = distance; + // Interpolant argument to constructor is not saved, so copy the factory method directly. + track.createInterpolant = this.createInterpolant; - } else { + return track; - targetArcLength = u * arcLengths[ il - 1 ]; + } - } + } ); - // binary search for the index with largest value smaller than target u distance + /** + * + * A Track of Boolean keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - var low = 0, high = il - 1, comparison; + function BooleanKeyframeTrack( name, times, values ) { - while ( low <= high ) { + KeyframeTrack.call( this, name, times, values ); - 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 + } - comparison = arcLengths[ i ] - targetArcLength; + BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - if ( comparison < 0 ) { + constructor: BooleanKeyframeTrack, - low = i + 1; + ValueTypeName: 'bool', + ValueBufferType: Array, - } else if ( comparison > 0 ) { + DefaultInterpolation: InterpolateDiscrete, - high = i - 1; + InterpolantFactoryMethodLinear: undefined, + InterpolantFactoryMethodSmooth: undefined - } else { + // Note: Actually this track could have a optimized / compressed + // representation of a single value and a custom interpolant that + // computes "firstValue ^ isOdd( index )". - high = i; - break; + } ); - // DONE + /** + * + * A Track of keyframe values that represent color. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - } + function ColorKeyframeTrack( name, times, values, interpolation ) { - } + KeyframeTrack.call( this, name, times, values, interpolation ); - i = high; + } - if ( arcLengths[ i ] === targetArcLength ) { + ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - return i / ( il - 1 ); + constructor: ColorKeyframeTrack, - } + ValueTypeName: 'color' - // we could get finer grain at lengths, or use simple interpolation between two points + // ValueBufferType is inherited - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; + // DefaultInterpolation is inherited - var segmentLength = lengthAfter - lengthBefore; + // Note: Very basic implementation and nothing special yet. + // However, this is the place for color space parameterization. - // determine where we are between the 'before' and 'after' points + } ); - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + /** + * + * A Track of numeric keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // add that fractional amount to t + function NumberKeyframeTrack( name, times, values, interpolation ) { - var t = ( i + segmentFraction ) / ( il - 1 ); + KeyframeTrack.call( this, name, times, values, interpolation ); - return t; + } - }, + NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - // 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 + constructor: NumberKeyframeTrack, - getTangent: function ( t ) { + ValueTypeName: 'number' - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; + // ValueBufferType is inherited - // Capping in case of danger + // DefaultInterpolation is inherited - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + } ); - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); + /** + * Spherical linear unit quaternion interpolant. + * + * @author tschw + */ - var vec = pt2.clone().sub( pt1 ); - return vec.normalize(); + function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - }, + Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - getTangentAt: function ( u ) { + } - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); + QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - }, + constructor: QuaternionLinearInterpolant, - computeFrenetFrames: function ( segments, closed ) { + interpolate_: function ( i1, t0, t, t1 ) { - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - var normal = new Vector3(); + offset = i1 * stride, - var tangents = []; - var normals = []; - var binormals = []; + alpha = ( t - t0 ) / ( t1 - t0 ); - var vec = new Vector3(); - var mat = new Matrix4(); + for ( var end = offset + stride; offset !== end; offset += 4 ) { - var i, u, theta; + Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - // compute the tangent vectors for each segment on the curve + } - for ( i = 0; i <= segments; i ++ ) { + return result; - u = i / segments; + } - tangents[ i ] = this.getTangentAt( u ); - tangents[ i ].normalize(); + } ); - } + /** + * + * A Track of quaternion keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component + function QuaternionKeyframeTrack( name, times, values, interpolation ) { - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - var min = Number.MAX_VALUE; - var tx = Math.abs( tangents[ 0 ].x ); - var ty = Math.abs( tangents[ 0 ].y ); - var tz = Math.abs( tangents[ 0 ].z ); + KeyframeTrack.call( this, name, times, values, interpolation ); - if ( tx <= min ) { + } - min = tx; - normal.set( 1, 0, 0 ); + QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - } + constructor: QuaternionKeyframeTrack, - if ( ty <= min ) { + ValueTypeName: 'quaternion', - min = ty; - normal.set( 0, 1, 0 ); + // ValueBufferType is inherited - } + DefaultInterpolation: InterpolateLinear, - if ( tz <= min ) { + InterpolantFactoryMethodLinear: function ( result ) { - normal.set( 0, 0, 1 ); + return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - } + }, - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + InterpolantFactoryMethodSmooth: undefined // not yet implemented - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + } ); + /** + * + * A Track that interpolates Strings + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // compute the slowly-varying normal and binormal vectors for each segment on the curve + function StringKeyframeTrack( name, times, values, interpolation ) { - for ( i = 1; i <= segments; i ++ ) { + KeyframeTrack.call( this, name, times, values, interpolation ); - normals[ i ] = normals[ i - 1 ].clone(); + } - binormals[ i ] = binormals[ i - 1 ].clone(); + StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + constructor: StringKeyframeTrack, - if ( vec.length() > Number.EPSILON ) { + ValueTypeName: 'string', + ValueBufferType: Array, - vec.normalize(); + DefaultInterpolation: InterpolateDiscrete, - theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + InterpolantFactoryMethodLinear: undefined, - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + InterpolantFactoryMethodSmooth: undefined - } + } ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + /** + * + * A Track of vectored keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - } + function VectorKeyframeTrack( name, times, values, interpolation ) { - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + KeyframeTrack.call( this, name, times, values, interpolation ); - if ( closed === true ) { + } - theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; + VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { + constructor: VectorKeyframeTrack, - theta = - theta; + ValueTypeName: 'vector' - } + // ValueBufferType is inherited - for ( i = 1; i <= segments; i ++ ) { + // DefaultInterpolation is inherited - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + } ); - } + /** + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - } + function AnimationClip( name, duration, tracks ) { - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; + this.name = name; + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : - 1; - }, + this.uuid = _Math.generateUUID(); - clone: function () { + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - return new this.constructor().copy( this ); + this.resetDuration(); - }, + } - copy: function ( source ) { + } - this.arcLengthDivisions = source.arcLengthDivisions; + function getTrackTypeForValueTypeName( typeName ) { - return this; + switch ( typeName.toLowerCase() ) { - }, + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': - toJSON: function () { + return NumberKeyframeTrack; - var data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; + return VectorKeyframeTrack; - return data; + case 'color': - }, + return ColorKeyframeTrack; - fromJSON: function ( json ) { + case 'quaternion': - this.arcLengthDivisions = json.arcLengthDivisions; + return QuaternionKeyframeTrack; - return this; + case 'bool': + case 'boolean': + + return BooleanKeyframeTrack; + + case 'string': + + return StringKeyframeTrack; } - } ); + throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + } - Curve.call( this ); + function parseKeyframeTrack( json ) { - this.type = 'EllipseCurve'; + if ( json.type === undefined ) { - this.aX = aX || 0; - this.aY = aY || 0; + throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; + } - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; + var trackType = getTrackTypeForValueTypeName( json.type ); - this.aClockwise = aClockwise || false; + if ( json.times === undefined ) { - this.aRotation = aRotation || 0; + var times = [], values = []; - } + AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; + json.times = times; + json.values = values; - EllipseCurve.prototype.isEllipseCurve = true; + } - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - var point = optionalTarget || new Vector2(); + return trackType.parse( json ); - var twoPi = Math.PI * 2; - var deltaAngle = this.aEndAngle - this.aStartAngle; - var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + } else { - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + // by default, we assume a constructor compatible with the base + return new trackType( json.name, json.times, json.values, json.interpolation ); - if ( deltaAngle < Number.EPSILON ) { + } - if ( samePoints ) { + } - deltaAngle = 0; + Object.assign( AnimationClip, { - } else { + parse: function ( json ) { - deltaAngle = twoPi; + var tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - } + for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { - } + tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - if ( this.aClockwise === true && ! samePoints ) { + } - if ( deltaAngle === twoPi ) { + return new AnimationClip( json.name, json.duration, tracks ); - deltaAngle = - twoPi; + }, - } else { + toJSON: function ( clip ) { - deltaAngle = deltaAngle - twoPi; + var tracks = [], + clipTracks = clip.tracks; - } + var json = { - } + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid - var angle = this.aStartAngle + t * deltaAngle; - var x = this.aX + this.xRadius * Math.cos( angle ); - var y = this.aY + this.yRadius * Math.sin( angle ); + }; - if ( this.aRotation !== 0 ) { + for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { - var cos = Math.cos( this.aRotation ); - var sin = Math.sin( this.aRotation ); + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - var tx = x - this.aX; - var ty = y - this.aY; + } - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; + return json; - } + }, - return point.set( x, y ); + CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { - }; + var numMorphTargets = morphTargetSequence.length; + var tracks = []; - EllipseCurve.prototype.copy = function ( source ) { + for ( var i = 0; i < numMorphTargets; i ++ ) { - Curve.prototype.copy.call( this, source ); + var times = []; + var values = []; - this.aX = source.aX; - this.aY = source.aY; + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; + values.push( 0, 1, 0 ); - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; + var order = AnimationUtils.getKeyframeOrder( times ); + times = AnimationUtils.sortedArray( times, 1, order ); + values = AnimationUtils.sortedArray( values, 1, order ); - this.aClockwise = source.aClockwise; + // 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 ) { - this.aRotation = source.aRotation; + times.push( numMorphTargets ); + values.push( values[ 0 ] ); - return this; + } - }; + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); + } - EllipseCurve.prototype.toJSON = function () { + return new AnimationClip( name, - 1, tracks ); - var data = Curve.prototype.toJSON.call( this ); + }, - data.aX = this.aX; - data.aY = this.aY; + findByName: function ( objectOrClipArray, name ) { - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; + var clipArray = objectOrClipArray; - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; + if ( ! Array.isArray( objectOrClipArray ) ) { - data.aClockwise = this.aClockwise; + var o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; - data.aRotation = this.aRotation; + } - return data; + for ( var i = 0; i < clipArray.length; i ++ ) { - }; + if ( clipArray[ i ].name === name ) { - EllipseCurve.prototype.fromJSON = function ( json ) { + return clipArray[ i ]; - Curve.prototype.fromJSON.call( this, json ); + } - this.aX = json.aX; - this.aY = json.aY; + } - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; + return null; - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; + }, - this.aClockwise = json.aClockwise; + CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { - this.aRotation = json.aRotation; + var animationToMorphTargets = {}; - return this; + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; - }; + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + if ( parts && parts.length > 1 ) { - this.type = 'ArcCurve'; + var name = parts[ 1 ]; - } + var animationMorphTargets = animationToMorphTargets[ name ]; + if ( ! animationMorphTargets ) { - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; + animationToMorphTargets[ name ] = animationMorphTargets = []; - ArcCurve.prototype.isArcCurve = true; + } - /** - * @author zz85 https://github.com/zz85 - * - * 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 - */ + animationMorphTargets.push( morphTarget ); + } - /* - 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. - */ + var clips = []; - function CubicPoly() { + for ( var name in animationToMorphTargets ) { - var c0 = 0, c1 = 0, c2 = 0, c3 = 0; + clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - /* - * 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 ) { + } - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; + return clips; - } + }, - return { + // parse the animation.hierarchy format + parseAnimation: function ( animation, bones ) { - initCatmullRom: function ( x0, x1, x2, x3, tension ) { + if ( ! animation ) { - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); + return null; - }, + } - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { + var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - // compute tangents when parameterized in [t1,t2] - var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + var times = []; + var values = []; - init( x1, x2, t1, t2 ); + AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); - }, + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - calc: function ( t ) { + destTracks.push( new trackType( trackName, times, values ) ); - var t2 = t * t; - var t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; + } - } + } - }; + }; - } + var tracks = []; - // + var clipName = animation.name || 'default'; + // automatic length determination in AnimationClip. + var duration = animation.length || - 1; + var fps = animation.fps || 30; - var tmp = new Vector3(); - var px = new CubicPoly(); - var py = new CubicPoly(); - var pz = new CubicPoly(); + var hierarchyTracks = animation.hierarchy || []; - function CatmullRomCurve3( points, closed, curveType, tension ) { + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { - Curve.call( this ); + var animationKeys = hierarchyTracks[ h ].keys; - this.type = 'CatmullRomCurve3'; + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - this.points = points || []; - this.closed = closed || false; - this.curveType = curveType || 'centripetal'; - this.tension = tension || 0.5; + // process morph targets + if ( animationKeys[ 0 ].morphTargets ) { - } + // figure out all morph targets used in this track + var morphTargetNames = {}; - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; + for ( var k = 0; k < animationKeys.length; k ++ ) { - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + if ( animationKeys[ k ].morphTargets ) { - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { + for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - var point = optionalTarget || new Vector3(); + morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - var points = this.points; - var l = points.length; + } - var p = ( l - ( this.closed ? 0 : 1 ) ) * t; - var intPoint = Math.floor( p ); - var weight = p - intPoint; + } - if ( this.closed ) { + } - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( var morphTargetName in morphTargetNames ) { - } else if ( weight === 0 && intPoint === l - 1 ) { + var times = []; + var values = []; - intPoint = l - 2; - weight = 1; + for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - } + var animationKey = animationKeys[ k ]; - var p0, p1, p2, p3; // 4 points + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - if ( this.closed || intPoint > 0 ) { + } - p0 = points[ ( intPoint - 1 ) % l ]; + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - } else { + } - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + duration = morphTargetNames.length * ( fps || 1.0 ); - } + } else { - p1 = points[ intPoint % l ]; - p2 = points[ ( intPoint + 1 ) % l ]; + // ...assume skeletal animation - if ( this.closed || intPoint + 2 < l ) { + var boneName = '.bones[' + bones[ h ].name + ']'; - p3 = points[ ( intPoint + 2 ) % l ]; + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); - } else { + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); - } + } - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { + } - // init Centripetal / Chordal Catmull-Rom - var pow = this.curveType === 'chordal' ? 0.5 : 0.25; - var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + if ( tracks.length === 0 ) { - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + return null; - 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 ); + } - } else if ( this.curveType === 'catmullrom' ) { + var clip = new AnimationClip( clipName, duration, tracks ); - 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 ); + return clip; } - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + } ); - return point; + Object.assign( AnimationClip.prototype, { - }; + resetDuration: function () { - CatmullRomCurve3.prototype.copy = function ( source ) { + var tracks = this.tracks, duration = 0; - Curve.prototype.copy.call( this, source ); + for ( var i = 0, n = tracks.length; i !== n; ++ i ) { - this.points = []; + var track = this.tracks[ i ]; - for ( var i = 0, l = source.points.length; i < l; i ++ ) { + duration = Math.max( duration, track.times[ track.times.length - 1 ] ); - var point = source.points[ i ]; + } - this.points.push( point.clone() ); + this.duration = duration; - } + return this; - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; + }, - return this; + trim: function () { - }; + for ( var i = 0; i < this.tracks.length; i ++ ) { - CatmullRomCurve3.prototype.toJSON = function () { + this.tracks[ i ].trim( 0, this.duration ); - var data = Curve.prototype.toJSON.call( this ); + } - data.points = []; + return this; - for ( var i = 0, l = this.points.length; i < l; i ++ ) { + }, - var point = this.points[ i ]; - data.points.push( point.toArray() ); + validate: function () { - } + var valid = true; - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; + for ( var i = 0; i < this.tracks.length; i ++ ) { - return data; + valid = valid && this.tracks[ i ].validate(); - }; + } - CatmullRomCurve3.prototype.fromJSON = function ( json ) { + return valid; - Curve.prototype.fromJSON.call( this, json ); + }, - this.points = []; + optimize: function () { - for ( var i = 0, l = json.points.length; i < l; i ++ ) { + for ( var i = 0; i < this.tracks.length; i ++ ) { - var point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); + this.tracks[ i ].optimize(); - } + } - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; + return this; - return this; + }, - }; - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ + clone: function () { - function CatmullRom( t, p0, p1, p2, p3 ) { + var tracks = []; - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + for ( var i = 0; i < this.tracks.length; i ++ ) { - } + tracks.push( this.tracks[ i ].clone() ); - // + } - function QuadraticBezierP0( t, p ) { + return new AnimationClip( this.name, this.duration, tracks ); - var k = 1 - t; - return k * k * p; + } - } + } ); - function QuadraticBezierP1( t, p ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - return 2 * ( 1 - t ) * t * p; + var Cache = { - } + enabled: false, - function QuadraticBezierP2( t, p ) { + files: {}, - return t * t * p; + add: function ( key, file ) { - } + if ( this.enabled === false ) return; - function QuadraticBezier( t, p0, p1, p2 ) { + // console.log( 'THREE.Cache', 'Adding key:', key ); - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); + this.files[ key ] = file; - } + }, - // + get: function ( key ) { - function CubicBezierP0( t, p ) { + if ( this.enabled === false ) return; - var k = 1 - t; - return k * k * k * p; + // console.log( 'THREE.Cache', 'Checking key:', key ); - } + return this.files[ key ]; - function CubicBezierP1( t, p ) { + }, - var k = 1 - t; - return 3 * k * k * t * p; + remove: function ( key ) { - } + delete this.files[ key ]; - function CubicBezierP2( t, p ) { + }, - return 3 * ( 1 - t ) * t * t * p; + clear: function () { - } + this.files = {}; - function CubicBezierP3( t, p ) { + } - return t * t * t * p; + }; - } + /** + * @author mrdoob / http://mrdoob.com/ + */ - function CubicBezier( t, p0, p1, p2, p3 ) { + function LoadingManager( onLoad, onProgress, onError ) { - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); + var scope = this; - } + var isLoading = false; + var itemsLoaded = 0; + var itemsTotal = 0; + var urlModifier = undefined; - function CubicBezierCurve( v0, v1, v2, v3 ) { + // Refer to #5689 for the reason why we don't set .onStart + // in the constructor - Curve.call( this ); + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - this.type = 'CubicBezierCurve'; + this.itemStart = function ( url ) { - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - this.v3 = v3 || new Vector2(); + itemsTotal ++; - } + if ( isLoading === false ) { - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; + if ( scope.onStart !== undefined ) { - CubicBezierCurve.prototype.isCubicBezierCurve = true; + scope.onStart( url, itemsLoaded, itemsTotal ); - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { + } - var point = optionalTarget || new Vector2(); + } - var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + isLoading = true; - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); + }; - return point; + this.itemEnd = function ( url ) { - }; + itemsLoaded ++; - CubicBezierCurve.prototype.copy = function ( source ) { + if ( scope.onProgress !== undefined ) { - Curve.prototype.copy.call( this, source ); + scope.onProgress( url, itemsLoaded, itemsTotal ); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + } - return this; + if ( itemsLoaded === itemsTotal ) { - }; + isLoading = false; - CubicBezierCurve.prototype.toJSON = function () { + if ( scope.onLoad !== undefined ) { - var data = Curve.prototype.toJSON.call( this ); + scope.onLoad(); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + } - return data; + } - }; + }; - CubicBezierCurve.prototype.fromJSON = function ( json ) { + this.itemError = function ( url ) { - Curve.prototype.fromJSON.call( this, json ); + if ( scope.onError !== undefined ) { - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + scope.onError( url ); - return this; + } - }; + }; - function CubicBezierCurve3( v0, v1, v2, v3 ) { + this.resolveURL = function ( url ) { - Curve.call( this ); + if ( urlModifier ) { - this.type = 'CubicBezierCurve3'; + return urlModifier( url ); - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - this.v3 = v3 || new Vector3(); + } - } + return url; - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; + }; - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; + this.setURLModifier = function ( transform ) { - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { + urlModifier = transform; + return this; - var point = optionalTarget || new Vector3(); + }; - var 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 ) - ); + var DefaultLoadingManager = new LoadingManager(); - return point; + /** + * @author mrdoob / http://mrdoob.com/ + */ - }; + var loading = {}; - CubicBezierCurve3.prototype.copy = function ( source ) { + function FileLoader( manager ) { - Curve.prototype.copy.call( this, source ); + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + } - return this; + Object.assign( FileLoader.prototype, { - }; + load: function ( url, onLoad, onProgress, onError ) { - CubicBezierCurve3.prototype.toJSON = function () { + if ( url === undefined ) url = ''; - var data = Curve.prototype.toJSON.call( this ); + if ( this.path !== undefined ) url = this.path + url; - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + url = this.manager.resolveURL( url ); - return data; + var scope = this; - }; + var cached = Cache.get( url ); - CubicBezierCurve3.prototype.fromJSON = function ( json ) { + if ( cached !== undefined ) { - Curve.prototype.fromJSON.call( this, json ); + scope.manager.itemStart( url ); - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + setTimeout( function () { - return this; + if ( onLoad ) onLoad( cached ); - }; + scope.manager.itemEnd( url ); - function LineCurve( v1, v2 ) { + }, 0 ); - Curve.call( this ); + return cached; - this.type = 'LineCurve'; + } - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); + // Check if request is duplicate - } + if ( loading[ url ] !== undefined ) { - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; + loading[ url ].push( { - LineCurve.prototype.isLineCurve = true; + onLoad: onLoad, + onProgress: onProgress, + onError: onError - LineCurve.prototype.getPoint = function ( t, optionalTarget ) { + } ); - var point = optionalTarget || new Vector2(); + return; - if ( t === 1 ) { + } - point.copy( this.v2 ); + // Check for data: URI + var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; + var dataUriRegexResult = url.match( dataUriRegex ); - } else { + // Safari can not handle Data URIs through XMLHttpRequest so process manually + if ( dataUriRegexResult ) { - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + var mimeType = dataUriRegexResult[ 1 ]; + var isBase64 = !! dataUriRegexResult[ 2 ]; + var data = dataUriRegexResult[ 3 ]; - } + data = decodeURIComponent( data ); - return point; + if ( isBase64 ) data = atob( data ); - }; + try { - // Line curve is linear, so we can overwrite default getPointAt + var response; + var responseType = ( this.responseType || '' ).toLowerCase(); - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { + switch ( responseType ) { - return this.getPoint( u, optionalTarget ); + case 'arraybuffer': + case 'blob': - }; + var view = new Uint8Array( data.length ); - LineCurve.prototype.getTangent = function ( /* t */ ) { + for ( var i = 0; i < data.length; i ++ ) { - var tangent = this.v2.clone().sub( this.v1 ); + view[ i ] = data.charCodeAt( i ); - return tangent.normalize(); + } - }; + if ( responseType === 'blob' ) { - LineCurve.prototype.copy = function ( source ) { + response = new Blob( [ view.buffer ], { type: mimeType } ); - Curve.prototype.copy.call( this, source ); + } else { - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + response = view.buffer; - return this; + } - }; + break; - LineCurve.prototype.toJSON = function () { + case 'document': - var data = Curve.prototype.toJSON.call( this ); + var parser = new DOMParser(); + response = parser.parseFromString( data, mimeType ); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + break; - return data; + case 'json': - }; + response = JSON.parse( data ); - LineCurve.prototype.fromJSON = function ( json ) { + break; - Curve.prototype.fromJSON.call( this, json ); + default: // 'text' or other - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + response = data; - return this; + break; - }; + } - function LineCurve3( v1, v2 ) { + // Wait for next browser tick like standard XMLHttpRequest event dispatching does + setTimeout( function () { - Curve.call( this ); + if ( onLoad ) onLoad( response ); - this.type = 'LineCurve3'; + scope.manager.itemEnd( url ); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); + }, 0 ); - } + } catch ( error ) { - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; + // Wait for next browser tick like standard XMLHttpRequest event dispatching does + setTimeout( function () { - LineCurve3.prototype.isLineCurve3 = true; + if ( onError ) onError( error ); - LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - var point = optionalTarget || new Vector3(); + }, 0 ); - if ( t === 1 ) { + } - point.copy( this.v2 ); + } else { - } else { + // Initialise array for duplicate requests - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + loading[ url ] = []; - } + loading[ url ].push( { - return point; + onLoad: onLoad, + onProgress: onProgress, + onError: onError - }; + } ); - // Line curve is linear, so we can overwrite default getPointAt + var request = new XMLHttpRequest(); - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { + request.open( 'GET', url, true ); - return this.getPoint( u, optionalTarget ); + request.addEventListener( 'load', function ( event ) { - }; + var response = this.response; - LineCurve3.prototype.copy = function ( source ) { + Cache.add( url, response ); - Curve.prototype.copy.call( this, source ); + var callbacks = loading[ url ]; - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + delete loading[ url ]; - return this; + 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. - LineCurve3.prototype.toJSON = function () { + if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - var data = Curve.prototype.toJSON.call( this ); + for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + var callback = callbacks[ i ]; + if ( callback.onLoad ) callback.onLoad( response ); - return data; + } - }; + scope.manager.itemEnd( url ); - LineCurve3.prototype.fromJSON = function ( json ) { + } else { - Curve.prototype.fromJSON.call( this, json ); + for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + var callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( event ); - return this; + } - }; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - function QuadraticBezierCurve( v0, v1, v2 ) { + } - Curve.call( this ); + }, false ); - this.type = 'QuadraticBezierCurve'; + request.addEventListener( 'progress', function ( event ) { - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); + var callbacks = loading[ url ]; - } + for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; + var callback = callbacks[ i ]; + if ( callback.onProgress ) callback.onProgress( event ); - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; + } - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { + }, false ); - var point = optionalTarget || new Vector2(); + request.addEventListener( 'error', function ( event ) { - var v0 = this.v0, v1 = this.v1, v2 = this.v2; + var callbacks = loading[ url ]; - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); + delete loading[ url ]; - return point; + for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - }; + var callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( event ); - QuadraticBezierCurve.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + }, false ); - return this; + request.addEventListener( 'abort', function ( event ) { - }; + var callbacks = loading[ url ]; - QuadraticBezierCurve.prototype.toJSON = function () { + delete loading[ url ]; - var data = Curve.prototype.toJSON.call( this ); + for ( var i = 0, il = callbacks.length; i < il; i ++ ) { - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + var callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( event ); - return data; + } - }; + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { + }, false ); - Curve.prototype.fromJSON.call( this, json ); + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); - return this; + for ( var header in this.requestHeader ) { - }; + request.setRequestHeader( header, this.requestHeader[ header ] ); - function QuadraticBezierCurve3( v0, v1, v2 ) { + } - Curve.call( this ); + request.send( null ); - this.type = 'QuadraticBezierCurve3'; + } - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); + scope.manager.itemStart( url ); - } + return request; - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; + }, - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + setPath: function ( value ) { - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { + this.path = value; + return this; - var point = optionalTarget || new Vector3(); + }, - var v0 = this.v0, v1 = this.v1, v2 = this.v2; + setResponseType: function ( value ) { - 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 ) - ); + this.responseType = value; + return this; - return point; + }, - }; + setWithCredentials: function ( value ) { - QuadraticBezierCurve3.prototype.copy = function ( source ) { + this.withCredentials = value; + return this; - Curve.prototype.copy.call( this, source ); + }, - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + setMimeType: function ( value ) { - return this; + this.mimeType = value; + return this; - }; + }, - QuadraticBezierCurve3.prototype.toJSON = function () { + setRequestHeader: function ( value ) { - var data = Curve.prototype.toJSON.call( this ); + this.requestHeader = value; + return this; - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + } - return data; + } ); - }; + /** + * @author bhouston / http://clara.io/ + */ - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { + function AnimationLoader( manager ) { - Curve.prototype.fromJSON.call( this, json ); + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } - return this; + Object.assign( AnimationLoader.prototype, { - }; + load: function ( url, onLoad, onProgress, onError ) { - function SplineCurve( points /* array of Vector2 */ ) { + var scope = this; - Curve.call( this ); + var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.load( url, function ( text ) { - this.type = 'SplineCurve'; + onLoad( scope.parse( JSON.parse( text ) ) ); - this.points = points || []; + }, onProgress, onError ); - } + }, - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; + parse: function ( json ) { - SplineCurve.prototype.isSplineCurve = true; + var animations = []; - SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { + for ( var i = 0; i < json.length; i ++ ) { - var point = optionalTarget || new Vector2(); + var clip = AnimationClip.parse( json[ i ] ); - var points = this.points; - var p = ( points.length - 1 ) * t; + animations.push( clip ); - var intPoint = Math.floor( p ); - var weight = p - intPoint; + } - var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - var p1 = points[ intPoint ]; - var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + return animations; - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); + }, - return point; + setPath: function ( value ) { - }; + this.path = value; + return this; - SplineCurve.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + } ); - this.points = []; + /** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ - for ( var i = 0, l = source.points.length; i < l; i ++ ) { + function CompressedTextureLoader( manager ) { - var point = source.points[ i ]; + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.points.push( point.clone() ); + // override in sub classes + this._parser = null; - } + } - return this; + Object.assign( CompressedTextureLoader.prototype, { - }; + load: function ( url, onLoad, onProgress, onError ) { - SplineCurve.prototype.toJSON = function () { + var scope = this; - var data = Curve.prototype.toJSON.call( this ); + var images = []; - data.points = []; + var texture = new CompressedTexture(); + texture.image = images; - for ( var i = 0, l = this.points.length; i < l; i ++ ) { + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); - var point = this.points[ i ]; - data.points.push( point.toArray() ); + function loadTexture( i ) { - } + loader.load( url[ i ], function ( buffer ) { - return data; + var texDatas = scope._parser( buffer, true ); - }; + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; - SplineCurve.prototype.fromJSON = function ( json ) { + loaded += 1; - Curve.prototype.fromJSON.call( this, json ); + if ( loaded === 6 ) { - this.points = []; + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = LinearFilter; - for ( var i = 0, l = json.points.length; i < l; i ++ ) { + texture.format = texDatas.format; + texture.needsUpdate = true; - var point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); + if ( onLoad ) onLoad( texture ); - } + } - return this; + }, onProgress, onError ); - }; + } + if ( Array.isArray( url ) ) { + var loaded = 0; - var Curves = Object.freeze({ - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); + for ( var i = 0, il = url.length; i < il; ++ i ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ + loadTexture( i ); - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + } - function CurvePath() { + } else { - Curve.call( this ); + // compressed cubemap texture stored in a single DDS file - this.type = 'CurvePath'; + loader.load( url, function ( buffer ) { - this.curves = []; - this.autoClose = false; // Automatically closes the path + var texDatas = scope._parser( buffer, true ); - } + if ( texDatas.isCubemap ) { - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - constructor: CurvePath, + for ( var f = 0; f < faces; f ++ ) { - add: function ( curve ) { + images[ f ] = { mipmaps: [] }; - this.curves.push( curve ); + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - }, + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; - closePath: function () { + } - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[ 0 ].getPoint( 0 ); - var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + } - if ( ! startPoint.equals( endPoint ) ) { + } else { - this.curves.push( new LineCurve( endPoint, startPoint ) ); + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + + } + + if ( texDatas.mipmapCount === 1 ) { + + texture.minFilter = LinearFilter; + + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, onProgress, onError ); } + return texture; + }, - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: + setPath: function ( value ) { - // 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.path = value; + return this; - getPoint: function ( t ) { + } - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0; + } ); - // To think about boundaries points. + /** + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + */ - while ( i < curveLengths.length ) { + function DataTextureLoader( manager ) { - if ( curveLengths[ i ] >= d ) { + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - var diff = curveLengths[ i ] - d; - var curve = this.curves[ i ]; + // override in sub classes + this._parser = null; - var segmentLength = curve.getLength(); - var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + } - return curve.getPointAt( u ); + Object.assign( DataTextureLoader.prototype, { - } + load: function ( url, onLoad, onProgress, onError ) { - i ++; + var scope = this; - } + var texture = new DataTexture(); - return null; + var loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + loader.load( url, function ( buffer ) { - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { + setTimeout( function () { - points.push( points[ 0 ] ); + if ( onLoad ) onLoad( cached ); - } + scope.manager.itemEnd( url ); - return points; + }, 0 ); - }, + return cached; - copy: function ( source ) { + } - Curve.prototype.copy.call( this, source ); + var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - this.curves = []; + function onImageLoad() { - for ( var i = 0, l = source.curves.length; i < l; i ++ ) { + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - var curve = source.curves[ i ]; + Cache.add( url, this ); - this.curves.push( curve.clone() ); + if ( onLoad ) onLoad( this ); + + scope.manager.itemEnd( url ); } - this.autoClose = source.autoClose; + function onImageError( event ) { - return this; + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - }, + if ( onError ) onError( event ); - toJSON: function () { + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - var data = Curve.prototype.toJSON.call( this ); + } - data.autoClose = this.autoClose; - data.curves = []; + image.addEventListener( 'load', onImageLoad, false ); + image.addEventListener( 'error', onImageError, false ); - for ( var i = 0, l = this.curves.length; i < l; i ++ ) { + if ( url.substr( 0, 5 ) !== 'data:' ) { - var curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; } - return data; + scope.manager.itemStart( url ); - }, + image.src = url; - fromJSON: function ( json ) { + return image; - Curve.prototype.fromJSON.call( this, json ); + }, - this.autoClose = json.autoClose; - this.curves = []; + setCrossOrigin: function ( value ) { - for ( var i = 0, l = json.curves.length; i < l; i ++ ) { + this.crossOrigin = value; + return this; - var curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); + }, - } + setPath: function ( value ) { + this.path = value; return this; } @@ -33052,172 +35064,131 @@ } ); /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - **/ + * @author mrdoob / http://mrdoob.com/ + */ - function Path( points ) { - CurvePath.call( this ); + function CubeTextureLoader( manager ) { - this.type = 'Path'; + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.currentPoint = new Vector2(); + } - if ( points ) { + Object.assign( CubeTextureLoader.prototype, { - this.setFromPoints( points ); + crossOrigin: 'anonymous', - } + load: function ( urls, onLoad, onProgress, onError ) { - } + var texture = new CubeTexture(); - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { + var loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - constructor: Path, + var loaded = 0; - setFromPoints: function ( points ) { + function loadTexture( i ) { - this.moveTo( points[ 0 ].x, points[ 0 ].y ); + loader.load( urls[ i ], function ( image ) { - for ( var i = 1, l = points.length; i < l; i ++ ) { + texture.images[ i ] = image; - this.lineTo( points[ i ].x, points[ i ].y ); + loaded ++; - } + if ( loaded === 6 ) { - }, + texture.needsUpdate = true; - moveTo: function ( x, y ) { + if ( onLoad ) onLoad( texture ); - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + } - }, + }, undefined, onError ); - lineTo: function ( x, y ) { + } - var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); + for ( var i = 0; i < urls.length; ++ i ) { - this.currentPoint.set( x, y ); + loadTexture( i ); - }, + } - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + return texture; - var curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); + }, - this.curves.push( curve ); + setCrossOrigin: function ( value ) { - this.currentPoint.set( aX, aY ); - - }, - - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - - var curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); - - this.curves.push( curve ); - - this.currentPoint.set( aX, aY ); + this.crossOrigin = value; + return this; }, - splineThru: function ( pts /*Array of Vector*/ ) { - - var npts = [ this.currentPoint.clone() ].concat( pts ); - - var curve = new SplineCurve( npts ); - this.curves.push( curve ); + setPath: function ( value ) { - this.currentPoint.copy( pts[ pts.length - 1 ] ); + this.path = value; + return this; - }, + } - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + } ); - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; + /** + * @author mrdoob / http://mrdoob.com/ + */ - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); - }, + function TextureLoader( manager ) { - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + } - }, + Object.assign( TextureLoader.prototype, { - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + crossOrigin: 'anonymous', - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; + load: function ( url, onLoad, onProgress, onError ) { - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + var texture = new Texture(); - }, + var loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + loader.load( url, function ( image ) { - var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + texture.image = image; - if ( this.curves.length > 0 ) { + // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. + var isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - // if a previous curve is present, attempt to join - var firstPoint = curve.getPoint( 0 ); + texture.format = isJPEG ? RGBFormat : RGBAFormat; + texture.needsUpdate = true; - if ( ! firstPoint.equals( this.currentPoint ) ) { + if ( onLoad !== undefined ) { - this.lineTo( firstPoint.x, firstPoint.y ); + onLoad( texture ); } - } - - this.curves.push( curve ); + }, onProgress, onError ); - var lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); + return texture; }, - copy: function ( source ) { - - CurvePath.prototype.copy.call( this, source ); - - this.currentPoint.copy( source.currentPoint ); + setCrossOrigin: function ( value ) { + this.crossOrigin = value; return this; }, - toJSON: function () { - - var data = CurvePath.prototype.toJSON.call( this ); - - data.currentPoint = this.currentPoint.toArray(); - - return data; - - }, - - fromJSON: function ( json ) { - - CurvePath.prototype.fromJSON.call( this, json ); - - this.currentPoint.fromArray( json.currentPoint ); + setPath: function ( value ) { + this.path = value; return this; } @@ -33226,416 +35197,416 @@ /** * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. + * Extensible curve object + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u ) + * .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. + * **/ - // STEP 1 Create a path. - // STEP 2 Turn path into shape. - // STEP 3 ExtrudeGeometry takes in Shape/Shapes - // STEP 3a - Extract points from each shape, turn to vertices - // STEP 3b - Triangulate each shape, add faces. - - function Shape( points ) { - - Path.call( this, points ); + /************************************************************** + * Abstract Curve base class + **************************************************************/ - this.uuid = _Math.generateUUID(); + function Curve() { - this.type = 'Shape'; + this.type = 'Curve'; - this.holes = []; + this.arcLengthDivisions = 200; } - Shape.prototype = Object.assign( Object.create( Path.prototype ), { + Object.assign( Curve.prototype, { - constructor: Shape, + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] - getPointsHoles: function ( divisions ) { + getPoint: function ( /* t, optionalTarget */ ) { - var holesPts = []; + console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + return null; - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + }, - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + // Get point at relative position in curve according to arc length + // - u [0 .. 1] - } + getPointAt: function ( u, optionalTarget ) { - return holesPts; + var t = this.getUtoTmapping( u ); + return this.getPoint( t, optionalTarget ); }, - // get points of shape and holes (keypoints based on segments parameter) - - extractPoints: function ( divisions ) { + // Get sequence of points using getPoint( t ) - return { + getPoints: function ( divisions ) { - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) + if ( divisions === undefined ) divisions = 5; - }; + var points = []; - }, + for ( var d = 0; d <= divisions; d ++ ) { - copy: function ( source ) { + points.push( this.getPoint( d / divisions ) ); - Path.prototype.copy.call( this, source ); + } - this.holes = []; + return points; - for ( var i = 0, l = source.holes.length; i < l; i ++ ) { + }, - var hole = source.holes[ i ]; + // Get sequence of points using getPointAt( u ) - this.holes.push( hole.clone() ); + getSpacedPoints: function ( divisions ) { - } + if ( divisions === undefined ) divisions = 5; - return this; + var points = []; - }, + for ( var d = 0; d <= divisions; d ++ ) { - toJSON: function () { + points.push( this.getPointAt( d / divisions ) ); - var data = Path.prototype.toJSON.call( this ); + } - data.uuid = this.uuid; - data.holes = []; + return points; - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + }, - var hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); + // Get total curve arc length - } + getLength: function () { - return data; + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; }, - fromJSON: function ( json ) { + // Get list of cumulative segment lengths - Path.prototype.fromJSON.call( this, json ); + getLengths: function ( divisions ) { - this.uuid = json.uuid; - this.holes = []; + if ( divisions === undefined ) divisions = this.arcLengthDivisions; - for ( var i = 0, l = json.holes.length; i < l; i ++ ) { + if ( this.cacheArcLengths && + ( this.cacheArcLengths.length === divisions + 1 ) && + ! this.needsUpdate ) { - var hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); + return this.cacheArcLengths; } - return this; - - } + this.needsUpdate = false; - } ); + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + cache.push( 0 ); - function Light( color, intensity ) { + for ( p = 1; p <= divisions; p ++ ) { - Object3D.call( this ); + current = this.getPoint( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; - this.type = 'Light'; + } - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; + this.cacheArcLengths = cache; - this.receiveShadow = undefined; + return cache; // { sums: cache, sum: sum }; Sum is in the last element. - } + }, - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { + updateArcLengths: function () { - constructor: Light, + this.needsUpdate = true; + this.getLengths(); - isLight: true, + }, - copy: function ( source ) { + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - Object3D.prototype.copy.call( this, source ); + getUtoTmapping: function ( u, distance ) { - this.color.copy( source.color ); - this.intensity = source.intensity; + var arcLengths = this.getLengths(); - return this; + var i = 0, il = arcLengths.length; - }, + var targetArcLength; // The targeted u distance value to get - toJSON: function ( meta ) { + if ( distance ) { - var data = Object3D.prototype.toJSON.call( this, meta ); + targetArcLength = distance; - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + } else { - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + targetArcLength = u * arcLengths[ il - 1 ]; - 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(); + // binary search for the index with largest value smaller than target u distance - return data; + var low = 0, high = il - 1, comparison; - } + 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 - /** - * @author alteredq / http://alteredqualia.com/ - */ + comparison = arcLengths[ i ] - targetArcLength; - function HemisphereLight( skyColor, groundColor, intensity ) { + if ( comparison < 0 ) { - Light.call( this, skyColor, intensity ); + low = i + 1; - this.type = 'HemisphereLight'; + } else if ( comparison > 0 ) { - this.castShadow = undefined; + high = i - 1; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + } else { - this.groundColor = new Color( groundColor ); + high = i; + break; - } + // DONE - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { + } - constructor: HemisphereLight, + } - isHemisphereLight: true, + i = high; - copy: function ( source ) { + if ( arcLengths[ i ] === targetArcLength ) { - Light.prototype.copy.call( this, source ); + return i / ( il - 1 ); - this.groundColor.copy( source.groundColor ); + } - return this; + // we could get finer grain at lengths, or use simple interpolation between two points - } + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; - } ); + var segmentLength = lengthAfter - lengthBefore; - /** - * @author mrdoob / http://mrdoob.com/ - */ + // determine where we are between the 'before' and 'after' points - function LightShadow( camera ) { + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - this.camera = camera; + // add that fractional amount to t - this.bias = 0; - this.radius = 1; + var t = ( i + segmentFraction ) / ( il - 1 ); - this.mapSize = new Vector2( 512, 512 ); + return t; - this.map = null; - this.matrix = new Matrix4(); + }, - } + // 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 - Object.assign( LightShadow.prototype, { + getTangent: function ( t ) { - copy: function ( source ) { + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; - this.camera = source.camera.clone(); + // Capping in case of danger - this.bias = source.bias; - this.radius = source.radius; + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - this.mapSize.copy( source.mapSize ); + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); - return this; + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); }, - clone: function () { + getTangentAt: function ( u ) { - return new this.constructor().copy( this ); + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); }, - toJSON: function () { - - var object = {}; + computeFrenetFrames: function ( segments, closed ) { - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + var normal = new Vector3(); - return object; + var tangents = []; + var normals = []; + var binormals = []; - } + var vec = new Vector3(); + var mat = new Matrix4(); - } ); + var i, u, theta; - /** - * @author mrdoob / http://mrdoob.com/ - */ + // compute the tangent vectors for each segment on the curve - function SpotLightShadow() { + for ( i = 0; i <= segments; i ++ ) { - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + u = i / segments; - } + tangents[ i ] = this.getTangentAt( u ); + tangents[ i ].normalize(); - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + } - constructor: SpotLightShadow, + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component - isSpotLightShadow: true, + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + var min = Number.MAX_VALUE; + var tx = Math.abs( tangents[ 0 ].x ); + var ty = Math.abs( tangents[ 0 ].y ); + var tz = Math.abs( tangents[ 0 ].z ); - update: function ( light ) { + if ( tx <= min ) { - var camera = this.camera; + min = tx; + normal.set( 1, 0, 0 ); - var fov = _Math.RAD2DEG * 2 * light.angle; - var aspect = this.mapSize.width / this.mapSize.height; - var far = light.distance || camera.far; + } - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + if ( ty <= min ) { - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); + min = ty; + normal.set( 0, 1, 0 ); } - } + if ( tz <= min ) { - } ); + normal.set( 0, 0, 1 ); - /** - * @author alteredq / http://alteredqualia.com/ - */ + } - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - Light.call( this, color, intensity ); + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - this.type = 'SpotLight'; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + // compute the slowly-varying normal and binormal vectors for each segment on the curve - this.target = new Object3D(); + for ( i = 1; i <= segments; i ++ ) { - Object.defineProperty( this, 'power', { - get: function () { + normals[ i ] = normals[ i - 1 ].clone(); - // 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; + binormals[ i ] = binormals[ i - 1 ].clone(); - }, - set: function ( power ) { + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - // 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; + if ( vec.length() > Number.EPSILON ) { - } - } ); + vec.normalize(); - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - this.shadow = new SpotLightShadow(); + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - } + } - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - constructor: SpotLight, + } - isSpotLight: true, + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - copy: function ( source ) { + if ( closed === true ) { - Light.prototype.copy.call( this, source ); + theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); + theta /= segments; - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - this.target = source.target.clone(); + theta = - theta; - this.shadow = source.shadow.clone(); + } - return this; + for ( i = 1; i <= segments; i ++ ) { - } + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - - function PointLight( color, intensity, distance, decay ) { + } - Light.call( this, color, intensity ); + } - this.type = 'PointLight'; + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; - Object.defineProperty( this, 'power', { - get: function () { + }, - // 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; + clone: function () { - }, - set: function ( power ) { + return new this.constructor().copy( this ); - // 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 ); + }, - } - } ); + copy: function ( source ) { - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + this.arcLengthDivisions = source.arcLengthDivisions; - this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + return this; - } + }, - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { + toJSON: function () { - constructor: PointLight, + var data = { + metadata: { + version: 4.5, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; - isPointLight: true, + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; - copy: function ( source ) { + return data; - Light.prototype.copy.call( this, source ); + }, - this.distance = source.distance; - this.decay = source.decay; + fromJSON: function ( json ) { - this.shadow = source.shadow.clone(); + this.arcLengthDivisions = json.arcLengthDivisions; return this; @@ -33643,1476 +35614,1471 @@ } ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - function DirectionalLightShadow( ) { + Curve.call( this ); - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + this.type = 'EllipseCurve'; + + this.aX = aX || 0; + this.aY = aY || 0; + + this.xRadius = xRadius || 1; + this.yRadius = yRadius || 1; + + this.aStartAngle = aStartAngle || 0; + this.aEndAngle = aEndAngle || 2 * Math.PI; + + this.aClockwise = aClockwise || false; + + this.aRotation = aRotation || 0; } - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + EllipseCurve.prototype = Object.create( Curve.prototype ); + EllipseCurve.prototype.constructor = EllipseCurve; - constructor: DirectionalLightShadow + EllipseCurve.prototype.isEllipseCurve = true; - } ); + EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + var point = optionalTarget || new Vector2(); - function DirectionalLight( color, intensity ) { + var twoPi = Math.PI * 2; + var deltaAngle = this.aEndAngle - this.aStartAngle; + var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - Light.call( this, color, intensity ); + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - this.type = 'DirectionalLight'; + if ( deltaAngle < Number.EPSILON ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + if ( samePoints ) { - this.target = new Object3D(); + deltaAngle = 0; - this.shadow = new DirectionalLightShadow(); + } else { - } + deltaAngle = twoPi; - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { + } - constructor: DirectionalLight, + } - isDirectionalLight: true, + if ( this.aClockwise === true && ! samePoints ) { - copy: function ( source ) { + if ( deltaAngle === twoPi ) { - Light.prototype.copy.call( this, source ); + deltaAngle = - twoPi; - this.target = source.target.clone(); + } else { - this.shadow = source.shadow.clone(); + deltaAngle = deltaAngle - twoPi; - return this; + } } - } ); + var angle = this.aStartAngle + t * deltaAngle; + var x = this.aX + this.xRadius * Math.cos( angle ); + var y = this.aY + this.yRadius * Math.sin( angle ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( this.aRotation !== 0 ) { - function AmbientLight( color, intensity ) { + var cos = Math.cos( this.aRotation ); + var sin = Math.sin( this.aRotation ); - Light.call( this, color, intensity ); + var tx = x - this.aX; + var ty = y - this.aY; - this.type = 'AmbientLight'; + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; - this.castShadow = undefined; + } - } + return point.set( x, y ); - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { + }; - constructor: AmbientLight, + EllipseCurve.prototype.copy = function ( source ) { - isAmbientLight: true + Curve.prototype.copy.call( this, source ); - } ); + this.aX = source.aX; + this.aY = source.aY; - /** - * @author abelnation / http://github.com/abelnation - */ + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; - function RectAreaLight( color, intensity, width, height ) { + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; - Light.call( this, color, intensity ); + this.aClockwise = source.aClockwise; - this.type = 'RectAreaLight'; + this.aRotation = source.aRotation; - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; + return this; - } + }; - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { - constructor: RectAreaLight, + EllipseCurve.prototype.toJSON = function () { - isRectAreaLight: true, + var data = Curve.prototype.toJSON.call( this ); - copy: function ( source ) { + data.aX = this.aX; + data.aY = this.aY; - Light.prototype.copy.call( this, source ); + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; - this.width = source.width; - this.height = source.height; + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; - return this; + data.aClockwise = this.aClockwise; - }, + data.aRotation = this.aRotation; - toJSON: function ( meta ) { + return data; - var data = Light.prototype.toJSON.call( this, meta ); + }; - data.object.width = this.width; - data.object.height = this.height; + EllipseCurve.prototype.fromJSON = function ( json ) { - return data; + Curve.prototype.fromJSON.call( this, json ); - } + this.aX = json.aX; + this.aY = json.aY; - } ); + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; - /** - * - * A Track that interpolates Strings - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; - function StringKeyframeTrack( name, times, values, interpolation ) { + this.aClockwise = json.aClockwise; - KeyframeTrack.call( this, name, times, values, interpolation ); + this.aRotation = json.aRotation; - } + return this; - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + }; - constructor: StringKeyframeTrack, + function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - ValueTypeName: 'string', - ValueBufferType: Array, + EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - DefaultInterpolation: InterpolateDiscrete, + this.type = 'ArcCurve'; - InterpolantFactoryMethodLinear: undefined, + } - InterpolantFactoryMethodSmooth: undefined + ArcCurve.prototype = Object.create( EllipseCurve.prototype ); + ArcCurve.prototype.constructor = ArcCurve; - } ); + ArcCurve.prototype.isArcCurve = true; /** + * @author zz85 https://github.com/zz85 * - * A Track of Boolean keyframe values. - * + * 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 * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 */ - function BooleanKeyframeTrack( name, times, values ) { - KeyframeTrack.call( this, name, times, values ); + /* + 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. + */ - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + function CubicPoly() { - constructor: BooleanKeyframeTrack, + var c0 = 0, c1 = 0, c2 = 0, c3 = 0; - ValueTypeName: 'bool', - ValueBufferType: Array, + /* + * 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 ) { - DefaultInterpolation: InterpolateDiscrete, + c0 = x0; + c1 = t0; + c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined + } - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". + return { - } ); + initCatmullRom: function ( x0, x1, x2, x3, tension ) { - /** - * 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 - * - * @author tschw - */ + init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + }, - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; + initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + // compute tangents when parameterized in [t1,t2] + var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - } + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - Object.assign( Interpolant.prototype, { + init( x1, x2, t1, t2 ); - evaluate: function ( t ) { + }, - var pp = this.parameterPositions, - i1 = this._cachedIndex, + calc: function ( t ) { - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + var t2 = t * t; + var t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; - validate_interval: { + } - seek: { + }; - var right; + } - linear_scan: { + // - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + var tmp = new Vector3(); + var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); - for ( var giveUpAt = i1 + 2; ; ) { + function CatmullRomCurve3( points, closed, curveType, tension ) { - if ( t1 === undefined ) { + Curve.call( this ); - if ( t < t0 ) break forward_scan; + this.type = 'CatmullRomCurve3'; - // after end + this.points = points || []; + this.closed = closed || false; + this.curveType = curveType || 'centripetal'; + this.tension = tension || 0.5; - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + } - } + CatmullRomCurve3.prototype = Object.create( Curve.prototype ); + CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; - if ( i1 === giveUpAt ) break; // this loop + CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; - t0 = t1; - t1 = pp[ ++ i1 ]; + CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { - if ( t < t1 ) { + var point = optionalTarget || new Vector3(); - // we have arrived at the sought interval - break seek; + var points = this.points; + var l = points.length; - } + var p = ( l - ( this.closed ? 0 : 1 ) ) * t; + var intPoint = Math.floor( p ); + var weight = p - intPoint; - } + if ( this.closed ) { - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - } + } else if ( weight === 0 && intPoint === l - 1 ) { - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + intPoint = l - 2; + weight = 1; - // looping? + } - var t1global = pp[ 1 ]; + var p0, p1, p2, p3; // 4 points - if ( t < t1global ) { + if ( this.closed || intPoint > 0 ) { - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + p0 = points[ ( intPoint - 1 ) % l ]; - } + } else { - // linear reverse scan + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; - for ( var giveUpAt = i1 - 2; ; ) { + } - if ( t0 === undefined ) { + p1 = points[ intPoint % l ]; + p2 = points[ ( intPoint + 1 ) % l ]; - // before start + if ( this.closed || intPoint + 2 < l ) { - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + p3 = points[ ( intPoint + 2 ) % l ]; - } + } else { - if ( i1 === giveUpAt ) break; // this loop + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + } - if ( t >= t0 ) { + if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - // we have arrived at the sought interval - break seek; + // init Centripetal / Chordal Catmull-Rom + var pow = this.curveType === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + var 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; - } + 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 ); - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + } else if ( this.curveType === 'catmullrom' ) { - } + 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 ); - // the interval is valid + } - break validate_interval; + point.set( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - } // linear scan + return point; - // binary search + }; - while ( i1 < right ) { + CatmullRomCurve3.prototype.copy = function ( source ) { - var mid = ( i1 + right ) >>> 1; + Curve.prototype.copy.call( this, source ); - if ( t < pp[ mid ] ) { + this.points = []; - right = mid; + for ( var i = 0, l = source.points.length; i < l; i ++ ) { - } else { + var point = source.points[ i ]; - i1 = mid + 1; + this.points.push( point.clone() ); - } + } - } + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + return this; - // check boundary cases, again + }; - if ( t0 === undefined ) { + CatmullRomCurve3.prototype.toJSON = function () { - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + var data = Curve.prototype.toJSON.call( this ); - } + data.points = []; - if ( t1 === undefined ) { + for ( var i = 0, l = this.points.length; i < l; i ++ ) { - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + var point = this.points[ i ]; + data.points.push( point.toArray() ); - } + } - } // seek + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; - this._cachedIndex = i1; + return data; - this.intervalChanged_( i1, t0, t1 ); + }; - } // validate_interval + CatmullRomCurve3.prototype.fromJSON = function ( json ) { - return this.interpolate_( i1, t0, t, t1 ); + Curve.prototype.fromJSON.call( this, json ); - }, + this.points = []; - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. + for ( var i = 0, l = json.points.length; i < l; i ++ ) { - // --- Protected interface + var point = json.points[ i ]; + this.points.push( new Vector3().fromArray( point ) ); - DefaultSettings_: {}, + } - getSettings_: function () { + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; - return this.settings || this.DefaultSettings_; + return this; - }, + }; - copySampleValue_: function ( index ) { + /** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Bezier Curves formulas obtained from + * http://en.wikipedia.org/wiki/Bézier_curve + */ - // copies a sample value to the result buffer + function CatmullRom( t, p0, p1, p2, p3 ) { - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - for ( var i = 0; i !== stride; ++ i ) { + } - result[ i ] = values[ offset + i ]; + // - } + function QuadraticBezierP0( t, p ) { - return result; + var k = 1 - t; + return k * k * p; - }, + } - // Template methods for derived classes: + function QuadraticBezierP1( t, p ) { - interpolate_: function ( /* i1, t0, t, t1 */ ) { + return 2 * ( 1 - t ) * t * p; - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer + } - }, + function QuadraticBezierP2( t, p ) { - intervalChanged_: function ( /* i1, t0, t1 */ ) { + return t * t * p; - // empty + } - } + function QuadraticBezier( t, p0, p1, p2 ) { - } ); + return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + + QuadraticBezierP2( t, p2 ); - //!\ DECLARE ALIAS AFTER assign prototype ! - Object.assign( Interpolant.prototype, { + } - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, + // - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, + function CubicBezierP0( t, p ) { - } ); + var k = 1 - t; + return k * k * k * p; - /** - * Spherical linear unit quaternion interpolant. - * - * @author tschw - */ + } - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + function CubicBezierP1( t, p ) { - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + var k = 1 - t; + return 3 * k * k * t * p; } - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + function CubicBezierP2( t, p ) { - constructor: QuaternionLinearInterpolant, + return 3 * ( 1 - t ) * t * t * p; - interpolate_: function ( i1, t0, t, t1 ) { + } - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + function CubicBezierP3( t, p ) { - offset = i1 * stride, + return t * t * t * p; - alpha = ( t - t0 ) / ( t1 - t0 ); + } - for ( var end = offset + stride; offset !== end; offset += 4 ) { + function CubicBezier( t, p0, p1, p2, p3 ) { - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); + return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + + CubicBezierP3( t, p3 ); - } + } - return result; + function CubicBezierCurve( v0, v1, v2, v3 ) { - } + Curve.call( this ); - } ); + this.type = 'CubicBezierCurve'; - /** - * - * A Track of quaternion keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + this.v0 = v0 || new Vector2(); + this.v1 = v1 || new Vector2(); + this.v2 = v2 || new Vector2(); + this.v3 = v3 || new Vector2(); - function QuaternionKeyframeTrack( name, times, values, interpolation ) { + } - KeyframeTrack.call( this, name, times, values, interpolation ); + CubicBezierCurve.prototype = Object.create( Curve.prototype ); + CubicBezierCurve.prototype.constructor = CubicBezierCurve; - } + CubicBezierCurve.prototype.isCubicBezierCurve = true; - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - constructor: QuaternionKeyframeTrack, + var point = optionalTarget || new Vector2(); - ValueTypeName: 'quaternion', + var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - // ValueBufferType is inherited + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) + ); - DefaultInterpolation: InterpolateLinear, + return point; - InterpolantFactoryMethodLinear: function ( result ) { + }; - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); + CubicBezierCurve.prototype.copy = function ( source ) { - }, + Curve.prototype.copy.call( this, source ); - InterpolantFactoryMethodSmooth: undefined // not yet implemented + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - } ); + return this; - /** - * - * A Track of keyframe values that represent color. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + }; - function ColorKeyframeTrack( name, times, values, interpolation ) { + CubicBezierCurve.prototype.toJSON = function () { - KeyframeTrack.call( this, name, times, values, interpolation ); + var data = Curve.prototype.toJSON.call( this ); - } + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + return data; - constructor: ColorKeyframeTrack, + }; - ValueTypeName: 'color' + CubicBezierCurve.prototype.fromJSON = function ( json ) { - // ValueBufferType is inherited + Curve.prototype.fromJSON.call( this, json ); - // DefaultInterpolation is inherited + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. + return this; - } ); + }; - /** - * - * A Track of numeric keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + function CubicBezierCurve3( v0, v1, v2, v3 ) { - function NumberKeyframeTrack( name, times, values, interpolation ) { + Curve.call( this ); - KeyframeTrack.call( this, name, times, values, interpolation ); + this.type = 'CubicBezierCurve3'; + + this.v0 = v0 || new Vector3(); + this.v1 = v1 || new Vector3(); + this.v2 = v2 || new Vector3(); + this.v3 = v3 || new Vector3(); } - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + CubicBezierCurve3.prototype = Object.create( Curve.prototype ); + CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; - constructor: NumberKeyframeTrack, + CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; - ValueTypeName: 'number' + CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - // ValueBufferType is inherited + var point = optionalTarget || new Vector3(); - // DefaultInterpolation is inherited + var 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 ) + ); - /** - * 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. - * - * @author tschw - */ + return point; - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + }; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + CubicBezierCurve3.prototype.copy = function ( source ) { - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; + Curve.prototype.copy.call( this, source ); - } + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + return this; - constructor: CubicInterpolant, + }; - DefaultSettings_: { + CubicBezierCurve3.prototype.toJSON = function () { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + var data = Curve.prototype.toJSON.call( this ); - }, + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - intervalChanged_: function ( i1, t0, t1 ) { + return data; - var pp = this.parameterPositions, - iPrev = i1 - 2, - iNext = i1 + 1, + }; - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; + CubicBezierCurve3.prototype.fromJSON = function ( json ) { - if ( tPrev === undefined ) { + Curve.prototype.fromJSON.call( this, json ); - switch ( this.getSettings_().endingStart ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - case ZeroSlopeEnding: + return this; - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + }; - break; + function LineCurve( v1, v2 ) { - case WrapAroundEnding: + Curve.call( this ); - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + this.type = 'LineCurve'; - break; + this.v1 = v1 || new Vector2(); + this.v2 = v2 || new Vector2(); - default: // ZeroCurvatureEnding + } - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; + LineCurve.prototype = Object.create( Curve.prototype ); + LineCurve.prototype.constructor = LineCurve; - } + LineCurve.prototype.isLineCurve = true; - } + LineCurve.prototype.getPoint = function ( t, optionalTarget ) { - if ( tNext === undefined ) { + var point = optionalTarget || new Vector2(); - switch ( this.getSettings_().endingEnd ) { + if ( t === 1 ) { - case ZeroSlopeEnding: + point.copy( this.v2 ); - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; + } else { - break; + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - case WrapAroundEnding: + } - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + return point; - break; + }; - default: // ZeroCurvatureEnding + // Line curve is linear, so we can overwrite default getPointAt - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { - } + return this.getPoint( u, optionalTarget ); - } + }; - var halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; + LineCurve.prototype.getTangent = function ( /* t */ ) { - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; + var tangent = this.v2.clone().sub( this.v1 ); - }, + return tangent.normalize(); - interpolate_: function ( i1, t0, t, t1 ) { + }; - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + LineCurve.prototype.copy = function ( source ) { - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, + Curve.prototype.copy.call( this, source ); - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - // evaluate polynomials + return this; - var sP = - wP * ppp + 2 * wP * pp - wP * p; - var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - var sN = wN * ppp - wN * pp; + }; - // combine data linearly + LineCurve.prototype.toJSON = function () { - for ( var i = 0; i !== stride; ++ i ) { + var data = Curve.prototype.toJSON.call( this ); - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - return result; + }; - } + LineCurve.prototype.fromJSON = function ( json ) { - } ); + Curve.prototype.fromJSON.call( this, json ); - /** - * @author tschw - */ + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + return this; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + }; - } + function LineCurve3( v1, v2 ) { - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + Curve.call( this ); - constructor: LinearInterpolant, + this.type = 'LineCurve3'; - interpolate_: function ( i1, t0, t, t1 ) { + this.v1 = v1 || new Vector3(); + this.v2 = v2 || new Vector3(); - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } - offset1 = i1 * stride, - offset0 = offset1 - stride, + LineCurve3.prototype = Object.create( Curve.prototype ); + LineCurve3.prototype.constructor = LineCurve3; - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + LineCurve3.prototype.isLineCurve3 = true; - for ( var i = 0; i !== stride; ++ i ) { + LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + var point = optionalTarget || new Vector3(); - } + if ( t === 1 ) { - return result; + point.copy( this.v2 ); - } + } else { - } ); + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - * - * @author tschw - */ + } - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + return point; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + }; - } + // Line curve is linear, so we can overwrite default getPointAt - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { - constructor: DiscreteInterpolant, + return this.getPoint( u, optionalTarget ); - interpolate_: function ( i1 /*, t0, t, t1 */ ) { + }; - return this.copySampleValue_( i1 - 1 ); + LineCurve3.prototype.copy = function ( source ) { - } + Curve.prototype.copy.call( this, source ); - } ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - /** - * @author tschw - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + return this; - var AnimationUtils = { + }; - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { + LineCurve3.prototype.toJSON = function () { - if ( AnimationUtils.isTypedArray( array ) ) { + var data = Curve.prototype.toJSON.call( this ); - // 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 ) ); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - return array.slice( from, to ); + }; - }, + LineCurve3.prototype.fromJSON = function ( json ) { - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { + Curve.prototype.fromJSON.call( this, json ); - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + return this; - return new type( array ); // create typed array + }; - } + function QuadraticBezierCurve( v0, v1, v2 ) { - return Array.prototype.slice.call( array ); // create Array + Curve.call( this ); - }, + this.type = 'QuadraticBezierCurve'; - isTypedArray: function ( object ) { + this.v0 = v0 || new Vector2(); + this.v1 = v1 || new Vector2(); + this.v2 = v2 || new Vector2(); - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + } - }, + QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); + QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { + QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; - function compareTime( i, j ) { + QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { - return times[ i ] - times[ j ]; + var point = optionalTarget || new Vector2(); - } + var v0 = this.v0, v1 = this.v1, v2 = this.v2; - var n = times.length; - var result = new Array( n ); - for ( var i = 0; i !== n; ++ i ) result[ i ] = i; + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ) + ); - result.sort( compareTime ); + return point; - return result; + }; - }, + QuadraticBezierCurve.prototype.copy = function ( source ) { - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { + Curve.prototype.copy.call( this, source ); - var nValues = values.length; - var result = new values.constructor( nValues ); + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + return this; - var srcOffset = order[ i ] * stride; + }; - for ( var j = 0; j !== stride; ++ j ) { + QuadraticBezierCurve.prototype.toJSON = function () { - result[ dstOffset ++ ] = values[ srcOffset + j ]; + var data = Curve.prototype.toJSON.call( this ); - } + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - } + return data; - return result; + }; - }, + QuadraticBezierCurve.prototype.fromJSON = function ( json ) { - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { + Curve.prototype.fromJSON.call( this, json ); - var i = 1, key = jsonKeys[ 0 ]; + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + return this; - key = jsonKeys[ i ++ ]; + }; - } + function QuadraticBezierCurve3( v0, v1, v2 ) { - if ( key === undefined ) return; // no data + Curve.call( this ); - var value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data + this.type = 'QuadraticBezierCurve3'; - if ( Array.isArray( value ) ) { + this.v0 = v0 || new Vector3(); + this.v1 = v1 || new Vector3(); + this.v2 = v2 || new Vector3(); - do { + } - value = key[ valuePropertyName ]; + QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); + QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; - if ( value !== undefined ) { + QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; - times.push( key.time ); - values.push.apply( values, value ); // push all elements + QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { - } + var point = optionalTarget || new Vector3(); - key = jsonKeys[ i ++ ]; + var v0 = this.v0, v1 = this.v1, v2 = this.v2; - } while ( key !== undefined ); + 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 ) + ); - } else if ( value.toArray !== undefined ) { + return point; - // ...assume THREE.Math-ish + }; - do { + QuadraticBezierCurve3.prototype.copy = function ( source ) { - value = key[ valuePropertyName ]; + Curve.prototype.copy.call( this, source ); - if ( value !== undefined ) { + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - times.push( key.time ); - value.toArray( values, values.length ); + return this; - } + }; - key = jsonKeys[ i ++ ]; + QuadraticBezierCurve3.prototype.toJSON = function () { - } while ( key !== undefined ); + var data = Curve.prototype.toJSON.call( this ); - } else { + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - // otherwise push as-is + return data; - do { + }; - value = key[ valuePropertyName ]; + QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { - if ( value !== undefined ) { + Curve.prototype.fromJSON.call( this, json ); - times.push( key.time ); - values.push( value ); + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - } + return this; - key = jsonKeys[ i ++ ]; + }; - } while ( key !== undefined ); + function SplineCurve( points /* array of Vector2 */ ) { - } + Curve.call( this ); - } + this.type = 'SplineCurve'; - }; + this.points = points || []; - /** - * - * A timed sequence of keyframes for a specific property. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function KeyframeTrack( name, times, values, interpolation ) { + SplineCurve.prototype = Object.create( Curve.prototype ); + SplineCurve.prototype.constructor = SplineCurve; - 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 ); + SplineCurve.prototype.isSplineCurve = true; - this.name = name; + SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); + var point = optionalTarget || new Vector2(); - this.setInterpolation( interpolation || this.DefaultInterpolation ); + var points = this.points; + var p = ( points.length - 1 ) * t; - this.validate(); - this.optimize(); + var intPoint = Math.floor( p ); + var weight = p - intPoint; - } + var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + var p1 = points[ intPoint ]; + var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - // Static methods: + point.set( + CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), + CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) + ); - Object.assign( KeyframeTrack, { + return point; - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + }; - parse: function ( json ) { + SplineCurve.prototype.copy = function ( source ) { - if ( json.type === undefined ) { + Curve.prototype.copy.call( this, source ); - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); + this.points = []; - } + for ( var i = 0, l = source.points.length; i < l; i ++ ) { - var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); + var point = source.points[ i ]; - if ( json.times === undefined ) { + this.points.push( point.clone() ); - var times = [], values = []; + } - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + return this; - json.times = times; - json.values = values; + }; - } + SplineCurve.prototype.toJSON = function () { - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + var data = Curve.prototype.toJSON.call( this ); - return trackType.parse( json ); + data.points = []; - } else { + for ( var i = 0, l = this.points.length; i < l; i ++ ) { - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + var point = this.points[ i ]; + data.points.push( point.toArray() ); - } + } - }, + return data; - toJSON: function ( track ) { + }; - var trackType = track.constructor; + SplineCurve.prototype.fromJSON = function ( json ) { - var json; + Curve.prototype.fromJSON.call( this, json ); - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { + this.points = []; - json = trackType.toJSON( track ); + for ( var i = 0, l = json.points.length; i < l; i ++ ) { - } else { + var point = json.points[ i ]; + this.points.push( new Vector2().fromArray( point ) ); - // by default, we assume the data can be serialized as-is - json = { + } - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) + return this; - }; + }; - var interpolation = track.getInterpolation(); - if ( interpolation !== track.DefaultInterpolation ) { - json.interpolation = interpolation; + var Curves = /*#__PURE__*/Object.freeze({ + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve + }); - } + /** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ - } + /************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ - json.type = track.ValueTypeName; // mandatory + function CurvePath() { - return json; + Curve.call( this ); - }, + this.type = 'CurvePath'; - _getTrackTypeForValueTypeName: function ( typeName ) { + this.curves = []; + this.autoClose = false; // Automatically closes the path - switch ( typeName.toLowerCase() ) { + } - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': + CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { - return NumberKeyframeTrack; + constructor: CurvePath, - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': + add: function ( curve ) { - return VectorKeyframeTrack; + this.curves.push( curve ); - case 'color': + }, - return ColorKeyframeTrack; + closePath: function () { - case 'quaternion': + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - return QuaternionKeyframeTrack; + if ( ! startPoint.equals( endPoint ) ) { - case 'bool': - case 'boolean': + this.curves.push( new LineCurve( endPoint, startPoint ) ); - return BooleanKeyframeTrack; + } - case 'string': + }, - return StringKeyframeTrack; + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: - } + // 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') - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); + getPoint: function ( t ) { - } + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0; - } ); + // To think about boundaries points. - Object.assign( KeyframeTrack.prototype, { + while ( i < curveLengths.length ) { - constructor: KeyframeTrack, + if ( curveLengths[ i ] >= d ) { - TimeBufferType: Float32Array, + var diff = curveLengths[ i ] - d; + var curve = this.curves[ i ]; - ValueBufferType: Float32Array, + var segmentLength = curve.getLength(); + var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - DefaultInterpolation: InterpolateLinear, + return curve.getPointAt( u ); - InterpolantFactoryMethodDiscrete: function ( result ) { + } - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); + i ++; - }, + } - InterpolantFactoryMethodLinear: function ( result ) { + return null; - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); + // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - getValueSize: function () { + points.push( points[ 0 ] ); - return this.values.length / this.times.length; + } + + return points; }, - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { + copy: function ( source ) { - if ( timeOffset !== 0.0 ) { + Curve.prototype.copy.call( this, source ); - var times = this.times; + this.curves = []; - for ( var i = 0, n = times.length; i !== n; ++ i ) { + for ( var i = 0, l = source.curves.length; i < l; i ++ ) { - times[ i ] += timeOffset; + var curve = source.curves[ i ]; - } + this.curves.push( curve.clone() ); } + this.autoClose = source.autoClose; + return this; }, - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { - - if ( timeScale !== 1.0 ) { + toJSON: function () { - var times = this.times; + var data = Curve.prototype.toJSON.call( this ); - for ( var i = 0, n = times.length; i !== n; ++ i ) { + data.autoClose = this.autoClose; + data.curves = []; - times[ i ] *= timeScale; + for ( var i = 0, l = this.curves.length; i < l; i ++ ) { - } + var curve = this.curves[ i ]; + data.curves.push( curve.toJSON() ); } - return this; + return data; }, - // 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: function ( startTime, endTime ) { + fromJSON: function ( json ) { - var times = this.times, - nKeys = times.length, - from = 0, - to = nKeys - 1; + Curve.prototype.fromJSON.call( this, json ); - while ( from !== nKeys && times[ from ] < startTime ) { + this.autoClose = json.autoClose; + this.curves = []; - ++ from; + for ( var i = 0, l = json.curves.length; i < l; i ++ ) { + + var curve = json.curves[ i ]; + this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); } - while ( to !== - 1 && times[ to ] > endTime ) { + return this; - -- to; + } - } + } ); - ++ to; // inclusive -> exclusive bound + /** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + **/ - if ( from !== 0 || to !== nKeys ) { + function Path( points ) { - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; + CurvePath.call( this ); - var stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); + this.type = 'Path'; - } + this.currentPoint = new Vector2(); - return this; + if ( points ) { - }, + this.setFromPoints( points ); - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { + } - var valid = true; - - var valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + } - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; + Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { - } + constructor: Path, - var times = this.times, - values = this.values, + setFromPoints: function ( points ) { - nKeys = times.length; + this.moveTo( points[ 0 ].x, points[ 0 ].y ); - if ( nKeys === 0 ) { + for ( var i = 1, l = points.length; i < l; i ++ ) { - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; + this.lineTo( points[ i ].x, points[ i ].y ); } - var prevTime = null; + }, - for ( var i = 0; i !== nKeys; i ++ ) { + moveTo: function ( x, y ) { - var currTime = times[ i ]; + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + }, - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; + lineTo: function ( x, y ) { - } + var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); + this.curves.push( curve ); - if ( prevTime !== null && prevTime > currTime ) { + this.currentPoint.set( x, y ); - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; + }, - } + quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - prevTime = currTime; + var curve = new QuadraticBezierCurve( + this.currentPoint.clone(), + new Vector2( aCPx, aCPy ), + new Vector2( aX, aY ) + ); - } + this.curves.push( curve ); - if ( values !== undefined ) { + this.currentPoint.set( aX, aY ); - if ( AnimationUtils.isTypedArray( values ) ) { + }, - for ( var i = 0, n = values.length; i !== n; ++ i ) { + bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - var value = values[ i ]; + var curve = new CubicBezierCurve( + this.currentPoint.clone(), + new Vector2( aCP1x, aCP1y ), + new Vector2( aCP2x, aCP2y ), + new Vector2( aX, aY ) + ); - if ( isNaN( value ) ) { + this.curves.push( curve ); - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; + this.currentPoint.set( aX, aY ); - } + }, - } + splineThru: function ( pts /*Array of Vector*/ ) { - } + var npts = [ this.currentPoint.clone() ].concat( pts ); - } + var curve = new SplineCurve( npts ); + this.curves.push( curve ); - return valid; + this.currentPoint.copy( pts[ pts.length - 1 ] ); }, - // 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: function () { - - var times = this.times, - values = this.values, - stride = this.getValueSize(), - - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - - writeIndex = 1, - lastIndex = times.length - 1; - - for ( var i = 1; i < lastIndex; ++ i ) { + arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - var keep = false; + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; - var time = times[ i ]; - var timeNext = times[ i + 1 ]; + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - // remove adjacent keyframes scheduled at the same time + }, - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { + absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - if ( ! smoothInterpolation ) { + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - // remove unnecessary keyframes same as their neighbors + }, - var offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - for ( var j = 0; j !== stride; ++ j ) { + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; - var value = values[ offset + j ]; + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + }, - keep = true; - break; + absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - } + var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - } + if ( this.curves.length > 0 ) { - } else { + // if a previous curve is present, attempt to join + var firstPoint = curve.getPoint( 0 ); - keep = true; + if ( ! firstPoint.equals( this.currentPoint ) ) { - } + this.lineTo( firstPoint.x, firstPoint.y ); } - // in-place compaction - - if ( keep ) { - - if ( i !== writeIndex ) { - - times[ writeIndex ] = times[ i ]; - - var readOffset = i * stride, - writeOffset = writeIndex * stride; - - for ( var j = 0; j !== stride; ++ j ) { - - values[ writeOffset + j ] = values[ readOffset + j ]; + } - } + this.curves.push( curve ); - } + var lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); - ++ writeIndex; + }, - } + copy: function ( source ) { - } + CurvePath.prototype.copy.call( this, source ); - // flush last keyframe (compaction looks ahead) + this.currentPoint.copy( source.currentPoint ); - if ( lastIndex > 0 ) { + return this; - times[ writeIndex ] = times[ lastIndex ]; + }, - for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { + toJSON: function () { - values[ writeOffset + j ] = values[ readOffset + j ]; + var data = CurvePath.prototype.toJSON.call( this ); - } + data.currentPoint = this.currentPoint.toArray(); - ++ writeIndex; + return data; - } + }, - if ( writeIndex !== times.length ) { + fromJSON: function ( json ) { - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + CurvePath.prototype.fromJSON.call( this, json ); - } + this.currentPoint.fromArray( json.currentPoint ); return this; @@ -35121,378 +37087,685 @@ } ); /** - * - * A Track of vectored keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ - function VectorKeyframeTrack( name, times, values, interpolation ) { + // STEP 1 Create a path. + // STEP 2 Turn path into shape. + // STEP 3 ExtrudeGeometry takes in Shape/Shapes + // STEP 3a - Extract points from each shape, turn to vertices + // STEP 3b - Triangulate each shape, add faces. - KeyframeTrack.call( this, name, times, values, interpolation ); + function Shape( points ) { + + Path.call( this, points ); + + this.uuid = _Math.generateUUID(); + + this.type = 'Shape'; + + this.holes = []; } - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + Shape.prototype = Object.assign( Object.create( Path.prototype ), { - constructor: VectorKeyframeTrack, + constructor: Shape, - ValueTypeName: 'vector' + getPointsHoles: function ( divisions ) { - // ValueBufferType is inherited + var holesPts = []; - // DefaultInterpolation is inherited + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - } ); + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); - /** - * - * Reusable set of Tracks that represent an animation. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + } - function AnimationClip( name, duration, tracks ) { + return holesPts; - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : - 1; + }, - this.uuid = _Math.generateUUID(); + // get points of shape and holes (keypoints based on segments parameter) - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + extractPoints: function ( divisions ) { - this.resetDuration(); + return { - } + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) - this.optimize(); + }; - } + }, - Object.assign( AnimationClip, { + copy: function ( source ) { - parse: function ( json ) { + Path.prototype.copy.call( this, source ); - var tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + this.holes = []; - for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { + for ( var i = 0, l = source.holes.length; i < l; i ++ ) { + + var hole = source.holes[ i ]; - tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); + this.holes.push( hole.clone() ); } - return new AnimationClip( json.name, json.duration, tracks ); + return this; }, - toJSON: function ( clip ) { - - var tracks = [], - clipTracks = clip.tracks; - - var json = { + toJSON: function () { - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks + var data = Path.prototype.toJSON.call( this ); - }; + data.uuid = this.uuid; + data.holes = []; - for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + var hole = this.holes[ i ]; + data.holes.push( hole.toJSON() ); } - return json; + return data; }, - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { - - var numMorphTargets = morphTargetSequence.length; - var tracks = []; - - for ( var i = 0; i < numMorphTargets; i ++ ) { + fromJSON: function ( json ) { - var times = []; - var values = []; + Path.prototype.fromJSON.call( this, json ); - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + this.uuid = json.uuid; + this.holes = []; - values.push( 0, 1, 0 ); + for ( var i = 0, l = json.holes.length; i < l; i ++ ) { - var order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); + var hole = json.holes[ i ]; + this.holes.push( new Path().fromJSON( hole ) ); - // 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 this; - } + } - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - return new AnimationClip( name, - 1, tracks ); + function Light( color, intensity ) { - }, + Object3D.call( this ); - findByName: function ( objectOrClipArray, name ) { + this.type = 'Light'; - var clipArray = objectOrClipArray; + this.color = new Color( color ); + this.intensity = intensity !== undefined ? intensity : 1; - if ( ! Array.isArray( objectOrClipArray ) ) { + this.receiveShadow = undefined; - var o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + } - } + Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - for ( var i = 0; i < clipArray.length; i ++ ) { + constructor: Light, - if ( clipArray[ i ].name === name ) { + isLight: true, - return clipArray[ i ]; + copy: function ( source ) { - } + Object3D.prototype.copy.call( this, source ); - } + this.color.copy( source.color ); + this.intensity = source.intensity; - return null; + return this; }, - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { + toJSON: function ( meta ) { - var animationToMorphTargets = {}; + var data = Object3D.prototype.toJSON.call( this, meta ); - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - var pattern = /^([\w-]*?)([\d]+)$/; + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - var morphTarget = morphTargets[ i ]; - var parts = morphTarget.name.match( pattern ); + 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 ( parts && parts.length > 1 ) { + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - var name = parts[ 1 ]; + return data; - var animationMorphTargets = animationToMorphTargets[ name ]; - if ( ! animationMorphTargets ) { + } - animationToMorphTargets[ name ] = animationMorphTargets = []; + } ); - } + /** + * @author alteredq / http://alteredqualia.com/ + */ - animationMorphTargets.push( morphTarget ); + function HemisphereLight( skyColor, groundColor, intensity ) { - } + Light.call( this, skyColor, intensity ); - } + this.type = 'HemisphereLight'; - var clips = []; + this.castShadow = undefined; - for ( var name in animationToMorphTargets ) { + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + this.groundColor = new Color( groundColor ); - } + } - return clips; + HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - }, + constructor: HemisphereLight, - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { + isHemisphereLight: true, - if ( ! animation ) { + copy: function ( source ) { - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; + Light.prototype.copy.call( this, source ); - } + this.groundColor.copy( source.groundColor ); - var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { + return this; - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + } - var times = []; - var values = []; + } ); - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); + /** + * @author mrdoob / http://mrdoob.com/ + */ - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + function LightShadow( camera ) { - destTracks.push( new trackType( trackName, times, values ) ); + this.camera = camera; - } + this.bias = 0; + this.radius = 1; - } + this.mapSize = new Vector2( 512, 512 ); - }; + this.map = null; + this.matrix = new Matrix4(); - var tracks = []; + } - var clipName = animation.name || 'default'; - // automatic length determination in AnimationClip. - var duration = animation.length || - 1; - var fps = animation.fps || 30; + Object.assign( LightShadow.prototype, { - var hierarchyTracks = animation.hierarchy || []; + copy: function ( source ) { - for ( var h = 0; h < hierarchyTracks.length; h ++ ) { + this.camera = source.camera.clone(); - var animationKeys = hierarchyTracks[ h ].keys; + this.bias = source.bias; + this.radius = source.radius; - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; + this.mapSize.copy( source.mapSize ); - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { + return this; - // figure out all morph targets used in this track - var morphTargetNames = {}; + }, - for ( var k = 0; k < animationKeys.length; k ++ ) { + clone: function () { - if ( animationKeys[ k ].morphTargets ) { + return new this.constructor().copy( this ); - for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { + }, - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; + toJSON: function () { - } + var object = {}; - } + if ( this.bias !== 0 ) object.bias = this.bias; + 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; - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( var morphTargetName in morphTargetNames ) { + return object; - var times = []; - var values = []; + } - for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { + } ); - var animationKey = animationKeys[ k ]; + /** + * @author mrdoob / http://mrdoob.com/ + */ - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); + function SpotLightShadow() { - } + LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); + } - } + SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - duration = morphTargetNames.length * ( fps || 1.0 ); + constructor: SpotLightShadow, - } else { + isSpotLightShadow: true, - // ...assume skeletal animation + update: function ( light ) { - var boneName = '.bones[' + bones[ h ].name + ']'; + var camera = this.camera; - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); + var fov = _Math.RAD2DEG * 2 * light.angle; + var aspect = this.mapSize.width / this.mapSize.height; + var far = light.distance || camera.far; - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); - } + } + + } + + } ); + + /** + * @author alteredq / http://alteredqualia.com/ + */ + + function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + + Light.call( this, color, intensity ); + + this.type = 'SpotLight'; + + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); + + this.target = new Object3D(); + + Object.defineProperty( this, 'power', { + get: function () { + + // 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: function ( power ) { + + // 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; } + } ); - if ( tracks.length === 0 ) { + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - return null; + this.shadow = new SpotLightShadow(); + + } + + SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { + + constructor: SpotLight, + + isSpotLight: true, + + copy: function ( source ) { + + Light.prototype.copy.call( this, source ); + + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; + + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + + } + + } ); + + /** + * @author mrdoob / http://mrdoob.com/ + */ + + + function PointLight( color, intensity, distance, decay ) { + + Light.call( this, color, intensity ); + + this.type = 'PointLight'; + + Object.defineProperty( this, 'power', { + get: function () { + + // 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; + + }, + set: function ( 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 + this.intensity = power / ( 4 * Math.PI ); } + } ); - var clip = new AnimationClip( clipName, duration, tracks ); + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - return clip; + this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + + } + + PointLight.prototype = Object.assign( Object.create( Light.prototype ), { + + constructor: PointLight, + + isPointLight: true, + + copy: function ( source ) { + + Light.prototype.copy.call( this, source ); + + this.distance = source.distance; + this.decay = source.decay; + + this.shadow = source.shadow.clone(); + + return this; } } ); - Object.assign( AnimationClip.prototype, { + /** + * @author alteredq / http://alteredqualia.com/ + * @author arose / http://github.com/arose + */ - resetDuration: function () { + function OrthographicCamera( left, right, top, bottom, near, far ) { - var tracks = this.tracks, duration = 0; + Camera.call( this ); - for ( var i = 0, n = tracks.length; i !== n; ++ i ) { + this.type = 'OrthographicCamera'; - var track = this.tracks[ i ]; + this.zoom = 1; + this.view = null; - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); + this.left = ( left !== undefined ) ? left : - 1; + this.right = ( right !== undefined ) ? right : 1; + this.top = ( top !== undefined ) ? top : 1; + this.bottom = ( bottom !== undefined ) ? bottom : - 1; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + + } + + OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { + + constructor: OrthographicCamera, + + isOrthographicCamera: true, + + copy: function ( source, recursive ) { + + Camera.prototype.copy.call( this, 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: function ( 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.duration = duration; + 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(); }, - trim: function () { + clearViewOffset: function () { - for ( var i = 0; i < this.tracks.length; i ++ ) { + if ( this.view !== null ) { - this.tracks[ i ].trim( 0, this.duration ); + this.view.enabled = false; } - return this; + this.updateProjectionMatrix(); }, - optimize: function () { + updateProjectionMatrix: function () { - for ( var i = 0; i < this.tracks.length; i ++ ) { + var dx = ( this.right - this.left ) / ( 2 * this.zoom ); + var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + var cx = ( this.right + this.left ) / 2; + var cy = ( this.top + this.bottom ) / 2; - this.tracks[ i ].optimize(); + var left = cx - dx; + var right = cx + dx; + var top = cy + dy; + var bottom = cy - dy; + + if ( this.view !== null && this.view.enabled ) { + + var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); + var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); + var scaleW = ( this.right - this.left ) / this.view.width; + var scaleH = ( this.top - this.bottom ) / this.view.height; + + left += scaleW * ( this.view.offsetX / zoomW ); + right = left + scaleW * ( this.view.width / zoomW ); + top -= scaleH * ( this.view.offsetY / zoomH ); + bottom = top - scaleH * ( this.view.height / zoomH ); } + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); + + this.projectionMatrixInverse.getInverse( this.projectionMatrix ); + + }, + + toJSON: function ( meta ) { + + var data = Object3D.prototype.toJSON.call( this, 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; + + } + + } ); + + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function DirectionalLightShadow( ) { + + LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + + } + + DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + + constructor: DirectionalLightShadow + + } ); + + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + + function DirectionalLight( color, intensity ) { + + Light.call( this, color, intensity ); + + this.type = 'DirectionalLight'; + + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); + + this.target = new Object3D(); + + this.shadow = new DirectionalLightShadow(); + + } + + DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { + + constructor: DirectionalLight, + + isDirectionalLight: true, + + copy: function ( source ) { + + Light.prototype.copy.call( this, source ); + + this.target = source.target.clone(); + + this.shadow = source.shadow.clone(); + + return this; + + } + + } ); + + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function AmbientLight( color, intensity ) { + + Light.call( this, color, intensity ); + + this.type = 'AmbientLight'; + + this.castShadow = undefined; + + } + + AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { + + constructor: AmbientLight, + + isAmbientLight: true + + } ); + + /** + * @author abelnation / http://github.com/abelnation + */ + + function RectAreaLight( color, intensity, width, height ) { + + Light.call( this, color, intensity ); + + this.type = 'RectAreaLight'; + + this.width = ( width !== undefined ) ? width : 10; + this.height = ( height !== undefined ) ? height : 10; + + } + + RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { + + constructor: RectAreaLight, + + isRectAreaLight: true, + + copy: function ( source ) { + + Light.prototype.copy.call( this, source ); + + this.width = source.width; + this.height = source.height; + return this; + }, + + toJSON: function ( meta ) { + + var data = Light.prototype.toJSON.call( this, meta ); + + data.object.width = this.width; + data.object.height = this.height; + + return data; + } } ); @@ -35515,6 +37788,7 @@ var scope = this; var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); @@ -35523,12 +37797,6 @@ }, - setTextures: function ( value ) { - - this.textures = value; - - }, - parse: function ( json ) { var textures = this.textures; @@ -35557,13 +37825,11 @@ if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; - if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.combine !== undefined ) material.combine = json.combine; if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; @@ -35594,6 +37860,68 @@ if ( json.visible !== undefined ) material.visible = json.visible; if ( json.userData !== undefined ) material.userData = json.userData; + // Shader Material + + if ( json.uniforms !== undefined ) { + + for ( var name in json.uniforms ) { + + var uniform = json.uniforms[ name ]; + + material.uniforms[ name ] = {}; + + switch ( uniform.type ) { + + case 't': + material.uniforms[ name ].value = getTexture( uniform.value ); + break; + + case 'c': + material.uniforms[ name ].value = new Color().setHex( uniform.value ); + break; + + case 'v2': + material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); + break; + + case 'v3': + material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); + break; + + case 'v4': + material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); + break; + + case 'm3': + material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); + + case 'm4': + material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); + break; + + default: + material.uniforms[ name ].value = uniform.value; + + } + + } + + } + + if ( json.defines !== undefined ) material.defines = json.defines; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + + if ( json.extensions !== undefined ) { + + for ( var key in json.extensions ) { + + material.extensions[ key ] = json.extensions[ key ]; + + } + + } + // Deprecated if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading @@ -35606,6 +37934,7 @@ // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); + if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); if ( json.alphaMap !== undefined ) { @@ -35618,6 +37947,7 @@ if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); + if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; if ( json.normalScale !== undefined ) { var normalScale = json.normalScale; @@ -35647,6 +37977,7 @@ if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); + if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; @@ -35660,10 +37991,67 @@ return material; + }, + + setPath: function ( value ) { + + this.path = value; + return this; + + }, + + setTextures: function ( value ) { + + this.textures = value; + return this; + } } ); + /** + * @author Don McCurdy / https://www.donmccurdy.com + */ + + var LoaderUtils = { + + decodeText: function ( array ) { + + if ( typeof TextDecoder !== 'undefined' ) { + + 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. + + var s = ''; + + for ( var i = 0, il = array.length; i < il; i ++ ) { + + // Implicitly assumes little-endian. + s += String.fromCharCode( array[ i ] ); + + } + + // Merges multi-byte utf-8 characters. + return decodeURIComponent( escape( s ) ); + + }, + + extractUrlBase: function ( url ) { + + var index = url.lastIndexOf( '/' ); + + if ( index === - 1 ) return './'; + + return url.substr( 0, index + 1 ); + + } + + }; + /** * @author mrdoob / http://mrdoob.com/ */ @@ -35681,6 +38069,7 @@ var scope = this; var loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); @@ -35709,7 +38098,36 @@ var attribute = attributes[ key ]; var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); + var bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); + if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; + geometry.addAttribute( key, bufferAttribute ); + + } + + var morphAttributes = json.data.morphAttributes; + + if ( morphAttributes ) { + + for ( var key in morphAttributes ) { + + var attributeArray = morphAttributes[ key ]; + + var array = []; + + for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { + + var attribute = attributeArray[ i ]; + var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); + + var bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); + if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; + array.push( bufferAttribute ); + + } + + geometry.morphAttributes[ key ] = array; + + } } @@ -35743,8 +38161,18 @@ } + if ( json.name ) geometry.name = json.name; + if ( json.userData ) geometry.userData = json.userData; + return geometry; + }, + + setPath: function ( value ) { + + this.path = value; + return this; + } } ); @@ -35763,729 +38191,558 @@ }; /** - * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ */ - function Loader() {} - - Loader.Handlers = { - - handlers: [], - - add: function ( regex, loader ) { - - this.handlers.push( regex, loader ); - - }, + function ObjectLoader( manager ) { - get: function ( file ) { + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + this.resourcePath = ''; - var handlers = this.handlers; + } - for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + Object.assign( ObjectLoader.prototype, { - var regex = handlers[ i ]; - var loader = handlers[ i + 1 ]; + crossOrigin: 'anonymous', - if ( regex.test( file ) ) { + load: function ( url, onLoad, onProgress, onError ) { - return loader; + var scope = this; - } + var path = ( this.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : this.path; + this.resourcePath = this.resourcePath || path; - } + var loader = new FileLoader( scope.manager ); + loader.setPath( this.path ); + loader.load( url, function ( text ) { - return null; + var json = null; - } + try { - }; + json = JSON.parse( text ); - Object.assign( Loader.prototype, { + } catch ( error ) { - crossOrigin: undefined, + if ( onError !== undefined ) onError( error ); - onLoadStart: function () {}, + console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - onLoadProgress: function () {}, + return; - onLoadComplete: function () {}, + } - initMaterials: function ( materials, texturePath, crossOrigin ) { + var metadata = json.metadata; - var array = []; + if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - for ( var i = 0; i < materials.length; ++ i ) { + console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); + return; - array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); + } - } + scope.parse( json, onLoad ); - return array; + }, onProgress, onError ); }, - createMaterial: ( function () { - - var BlendingMode = { - NoBlending: NoBlending, - NormalBlending: NormalBlending, - AdditiveBlending: AdditiveBlending, - SubtractiveBlending: SubtractiveBlending, - MultiplyBlending: MultiplyBlending, - CustomBlending: CustomBlending - }; + setPath: function ( value ) { - var color = new Color(); - var textureLoader = new TextureLoader(); - var materialLoader = new MaterialLoader(); + this.path = value; + return this; - return function createMaterial( m, texturePath, crossOrigin ) { + }, - // convert from old material format + setResourcePath: function ( value ) { - var textures = {}; + this.resourcePath = value; + return this; - function loadTexture( path, repeat, offset, wrap, anisotropy ) { + }, - var fullPath = texturePath + path; - var loader = Loader.Handlers.get( fullPath ); + setCrossOrigin: function ( value ) { - var texture; + this.crossOrigin = value; + return this; - if ( loader !== null ) { + }, - texture = loader.load( fullPath ); + parse: function ( json, onLoad ) { - } else { + var shapes = this.parseShape( json.shapes ); + var geometries = this.parseGeometries( json.geometries, shapes ); - textureLoader.setCrossOrigin( crossOrigin ); - texture = textureLoader.load( fullPath ); + var images = this.parseImages( json.images, function () { - } + if ( onLoad !== undefined ) onLoad( object ); - if ( repeat !== undefined ) { + } ); - texture.repeat.fromArray( repeat ); + var textures = this.parseTextures( json.textures, images ); + var materials = this.parseMaterials( json.materials, textures ); - if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; + var object = this.parseObject( json.object, geometries, materials ); - } + if ( json.animations ) { - if ( offset !== undefined ) { + object.animations = this.parseAnimations( json.animations ); - texture.offset.fromArray( offset ); + } - } + if ( json.images === undefined || json.images.length === 0 ) { - if ( wrap !== undefined ) { + if ( onLoad !== undefined ) onLoad( object ); - if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; - if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; + } - if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; - if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; + return object; - } + }, - if ( anisotropy !== undefined ) { + parseShape: function ( json ) { - texture.anisotropy = anisotropy; + var shapes = {}; - } + if ( json !== undefined ) { - var uuid = _Math.generateUUID(); + for ( var i = 0, l = json.length; i < l; i ++ ) { - textures[ uuid ] = texture; + var shape = new Shape().fromJSON( json[ i ] ); - return uuid; + shapes[ shape.uuid ] = shape; } - // + } - var json = { - uuid: _Math.generateUUID(), - type: 'MeshLambertMaterial' - }; + return shapes; - for ( var name in m ) { + }, - var value = m[ name ]; + parseGeometries: function ( json, shapes ) { - switch ( name ) { + var geometries = {}; - case 'DbgColor': - case 'DbgIndex': - case 'opticalDensity': - case 'illumination': - break; - case 'DbgName': - json.name = value; - break; - case 'blending': - json.blending = BlendingMode[ value ]; - break; - case 'colorAmbient': - case 'mapAmbient': - console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); - break; - case 'colorDiffuse': - json.color = color.fromArray( value ).getHex(); - break; - case 'colorSpecular': - json.specular = color.fromArray( value ).getHex(); - break; - case 'colorEmissive': - json.emissive = color.fromArray( value ).getHex(); - break; - case 'specularCoef': - json.shininess = value; - break; - case 'shading': - if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; - if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; - if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; - break; - case 'mapDiffuse': - json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); - break; - case 'mapDiffuseRepeat': - case 'mapDiffuseOffset': - case 'mapDiffuseWrap': - case 'mapDiffuseAnisotropy': - break; - case 'mapEmissive': - json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); - break; - case 'mapEmissiveRepeat': - case 'mapEmissiveOffset': - case 'mapEmissiveWrap': - case 'mapEmissiveAnisotropy': - break; - case 'mapLight': - json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); - break; - case 'mapLightRepeat': - case 'mapLightOffset': - case 'mapLightWrap': - case 'mapLightAnisotropy': - break; - case 'mapAO': - json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); - break; - case 'mapAORepeat': - case 'mapAOOffset': - case 'mapAOWrap': - case 'mapAOAnisotropy': - break; - case 'mapBump': - json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); - break; - case 'mapBumpScale': - json.bumpScale = value; - break; - case 'mapBumpRepeat': - case 'mapBumpOffset': - case 'mapBumpWrap': - case 'mapBumpAnisotropy': - break; - case 'mapNormal': - json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); - break; - case 'mapNormalFactor': - json.normalScale = value; - break; - case 'mapNormalRepeat': - case 'mapNormalOffset': - case 'mapNormalWrap': - case 'mapNormalAnisotropy': - break; - case 'mapSpecular': - json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); - break; - case 'mapSpecularRepeat': - case 'mapSpecularOffset': - case 'mapSpecularWrap': - case 'mapSpecularAnisotropy': - break; - case 'mapMetalness': - json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); - break; - case 'mapMetalnessRepeat': - case 'mapMetalnessOffset': - case 'mapMetalnessWrap': - case 'mapMetalnessAnisotropy': - break; - case 'mapRoughness': - json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); - break; - case 'mapRoughnessRepeat': - case 'mapRoughnessOffset': - case 'mapRoughnessWrap': - case 'mapRoughnessAnisotropy': - break; - case 'mapAlpha': - json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); - break; - case 'mapAlphaRepeat': - case 'mapAlphaOffset': - case 'mapAlphaWrap': - case 'mapAlphaAnisotropy': - break; - case 'flipSided': - json.side = BackSide; - break; - case 'doubleSided': - json.side = DoubleSide; - break; - case 'transparency': - console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); - json.opacity = value; - break; - case 'depthTest': - case 'depthWrite': - case 'colorWrite': - case 'opacity': - case 'reflectivity': - case 'transparent': - case 'visible': - case 'wireframe': - json[ name ] = value; - break; - case 'vertexColors': - if ( value === true ) json.vertexColors = VertexColors; - if ( value === 'face' ) json.vertexColors = FaceColors; - break; - default: - console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); - break; + if ( json !== undefined ) { - } + var bufferGeometryLoader = new BufferGeometryLoader(); - } + for ( var i = 0, l = json.length; i < l; i ++ ) { - if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; - if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; + var geometry; + var data = json[ i ]; - if ( json.opacity < 1 ) json.transparent = true; + switch ( data.type ) { - materialLoader.setTextures( textures ); + case 'PlaneGeometry': + case 'PlaneBufferGeometry': - return materialLoader.parse( json ); + geometry = new Geometries[ data.type ]( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); - }; + break; - } )() + case 'BoxGeometry': + case 'BoxBufferGeometry': + case 'CubeGeometry': // backwards compatible - } ); + geometry = new Geometries[ data.type ]( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); - /** - * @author Don McCurdy / https://www.donmccurdy.com - */ + break; - var LoaderUtils = { + case 'CircleGeometry': + case 'CircleBufferGeometry': - decodeText: function ( array ) { + geometry = new Geometries[ data.type ]( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); - if ( typeof TextDecoder !== 'undefined' ) { + break; - return new TextDecoder().decode( array ); + case 'CylinderGeometry': + case 'CylinderBufferGeometry': - } + geometry = new Geometries[ data.type ]( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. + break; - var s = ''; + case 'ConeGeometry': + case 'ConeBufferGeometry': - for ( var i = 0, il = array.length; i < il; i ++ ) { + geometry = new Geometries[ data.type ]( + data.radius, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); + break; - } + case 'SphereGeometry': + case 'SphereBufferGeometry': - // Merges multi-byte utf-8 characters. - return decodeURIComponent( escape( s ) ); + geometry = new Geometries[ data.type ]( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); - }, + break; - extractUrlBase: function ( url ) { + case 'DodecahedronGeometry': + case 'DodecahedronBufferGeometry': + case 'IcosahedronGeometry': + case 'IcosahedronBufferGeometry': + case 'OctahedronGeometry': + case 'OctahedronBufferGeometry': + case 'TetrahedronGeometry': + case 'TetrahedronBufferGeometry': - var index = url.lastIndexOf( '/' ); + geometry = new Geometries[ data.type ]( + data.radius, + data.detail + ); - if ( index === - 1 ) return './'; + break; - return url.substr( 0, index + 1 ); + case 'RingGeometry': + case 'RingBufferGeometry': - } + geometry = new Geometries[ data.type ]( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); - }; + break; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + case 'TorusGeometry': + case 'TorusBufferGeometry': - function JSONLoader( manager ) { + geometry = new Geometries[ data.type ]( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); - if ( typeof manager === 'boolean' ) { + break; - console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); - manager = undefined; + case 'TorusKnotGeometry': + case 'TorusKnotBufferGeometry': - } + geometry = new Geometries[ data.type ]( + data.radius, + data.tube, + data.tubularSegments, + data.radialSegments, + data.p, + data.q + ); - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + break; - this.withCredentials = false; + case 'TubeGeometry': + case 'TubeBufferGeometry': - } + // This only works for built-in curves (e.g. CatmullRomCurve3). + // User defined curves or instances of CurvePath will not be deserialized. + geometry = new Geometries[ data.type ]( + new Curves[ data.path.type ]().fromJSON( data.path ), + data.tubularSegments, + data.radius, + data.radialSegments, + data.closed + ); - Object.assign( JSONLoader.prototype, { + break; - load: function ( url, onLoad, onProgress, onError ) { + case 'LatheGeometry': + case 'LatheBufferGeometry': - var scope = this; + geometry = new Geometries[ data.type ]( + data.points, + data.segments, + data.phiStart, + data.phiLength + ); - var texturePath = this.texturePath && ( typeof this.texturePath === 'string' ) ? this.texturePath : LoaderUtils.extractUrlBase( url ); + break; - var loader = new FileLoader( this.manager ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( text ) { + case 'PolyhedronGeometry': + case 'PolyhedronBufferGeometry': - var json = JSON.parse( text ); - var metadata = json.metadata; + geometry = new Geometries[ data.type ]( + data.vertices, + data.indices, + data.radius, + data.details + ); - if ( metadata !== undefined ) { + break; - var type = metadata.type; + case 'ShapeGeometry': + case 'ShapeBufferGeometry': - if ( type !== undefined ) { + var geometryShapes = []; - if ( type.toLowerCase() === 'object' ) { + for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); - return; + var shape = shapes[ data.shapes[ j ] ]; - } + geometryShapes.push( shape ); - } + } - } + geometry = new Geometries[ data.type ]( + geometryShapes, + data.curveSegments + ); - var object = scope.parse( json, texturePath ); - onLoad( object.geometry, object.materials ); + break; - }, onProgress, onError ); - }, + case 'ExtrudeGeometry': + case 'ExtrudeBufferGeometry': - setTexturePath: function ( value ) { + var geometryShapes = []; - this.texturePath = value; + for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { - }, + var shape = shapes[ data.shapes[ j ] ]; - parse: ( function () { + geometryShapes.push( shape ); - function parseModel( json, geometry ) { + } - function isBitSet( value, position ) { + var extrudePath = data.options.extrudePath; - return value & ( 1 << position ); + if ( extrudePath !== undefined ) { - } + data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); - var i, j, fi, + } - offset, zLength, + geometry = new Geometries[ data.type ]( + geometryShapes, + data.options + ); - colorIndex, normalIndex, uvIndex, materialIndex, + break; - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, + case 'BufferGeometry': - vertex, face, faceA, faceB, hex, normal, + geometry = bufferGeometryLoader.parse( data ); - uvLayer, uv, u, v, + break; - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, + case 'Geometry': - scale = json.scale, + if ( 'THREE' in window && 'LegacyJSONLoader' in THREE ) { - nUvLayers = 0; + var geometryLoader = new THREE.LegacyJSONLoader(); + geometry = geometryLoader.parse( data, this.resourcePath ).geometry; - if ( json.uvs !== undefined ) { + } else { - // disregard empty arrays + console.error( 'THREE.ObjectLoader: You have to import LegacyJSONLoader in order load geometry data of type "Geometry".' ); - for ( i = 0; i < json.uvs.length; i ++ ) { + } - if ( json.uvs[ i ].length ) nUvLayers ++; + break; - } + default: - for ( i = 0; i < nUvLayers; i ++ ) { + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - geometry.faceVertexUvs[ i ] = []; + continue; } - } - - offset = 0; - zLength = vertices.length; - - while ( offset < zLength ) { - - vertex = new Vector3(); + geometry.uuid = data.uuid; - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; + if ( data.name !== undefined ) geometry.name = data.name; + if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; - geometry.vertices.push( vertex ); + geometries[ data.uuid ] = geometry; } - offset = 0; - zLength = faces.length; - - while ( offset < zLength ) { - - type = faces[ offset ++ ]; - - isQuad = isBitSet( type, 0 ); - hasMaterial = isBitSet( type, 1 ); - hasFaceVertexUv = isBitSet( type, 3 ); - hasFaceNormal = isBitSet( type, 4 ); - hasFaceVertexNormal = isBitSet( type, 5 ); - hasFaceColor = isBitSet( type, 6 ); - hasFaceVertexColor = isBitSet( type, 7 ); - - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - - if ( isQuad ) { - - faceA = new Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; - - faceB = new Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; - - offset += 4; + } - if ( hasMaterial ) { + return geometries; - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; + }, - } + parseMaterials: function ( json, textures ) { - // to get face <=> uv index correspondence + var cache = {}; // MultiMaterial + var materials = {}; - fi = geometry.faces.length; + if ( json !== undefined ) { - if ( hasFaceVertexUv ) { + var loader = new MaterialLoader(); + loader.setTextures( textures ); - for ( i = 0; i < nUvLayers; i ++ ) { + for ( var i = 0, l = json.length; i < l; i ++ ) { - uvLayer = json.uvs[ i ]; + var data = json[ i ]; - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + if ( data.type === 'MultiMaterial' ) { - for ( j = 0; j < 4; j ++ ) { + // Deprecated - uvIndex = faces[ offset ++ ]; + var array = []; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + for ( var j = 0; j < data.materials.length; j ++ ) { - uv = new Vector2( u, v ); + var material = data.materials[ j ]; - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + if ( cache[ material.uuid ] === undefined ) { - } + cache[ material.uuid ] = loader.parse( material ); } - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - faceB.normal.copy( faceA.normal ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 4; i ++ ) { - - normalIndex = faces[ offset ++ ] * 3; - - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); - - } + array.push( cache[ material.uuid ] ); } + materials[ data.uuid ] = array; - if ( hasFaceColor ) { + } else { - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + if ( cache[ data.uuid ] === undefined ) { - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); + cache[ data.uuid ] = loader.parse( data ); } + materials[ data.uuid ] = cache[ data.uuid ]; - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 4; i ++ ) { - - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; - - if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); + } - } + } - } + } - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); + return materials; - } else { + }, - face = new Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; + parseAnimations: function ( json ) { - if ( hasMaterial ) { + var animations = []; - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; + for ( var i = 0; i < json.length; i ++ ) { - } + var data = json[ i ]; - // to get face <=> uv index correspondence + var clip = AnimationClip.parse( data ); - fi = geometry.faces.length; + if ( data.uuid !== undefined ) clip.uuid = data.uuid; - if ( hasFaceVertexUv ) { + animations.push( clip ); - for ( i = 0; i < nUvLayers; i ++ ) { + } - uvLayer = json.uvs[ i ]; + return animations; - geometry.faceVertexUvs[ i ][ fi ] = []; + }, - for ( j = 0; j < 3; j ++ ) { + parseImages: function ( json, onLoad ) { - uvIndex = faces[ offset ++ ]; + var scope = this; + var images = {}; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + function loadImage( url ) { - uv = new Vector2( u, v ); + scope.manager.itemStart( url ); - geometry.faceVertexUvs[ i ][ fi ].push( uv ); + return loader.load( url, function () { - } + scope.manager.itemEnd( url ); - } + }, undefined, function () { - } + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - if ( hasFaceNormal ) { + } ); - normalIndex = faces[ offset ++ ] * 3; + } - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + if ( json !== undefined && json.length > 0 ) { - } + var manager = new LoadingManager( onLoad ); - if ( hasFaceVertexNormal ) { + var loader = new ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); - for ( i = 0; i < 3; i ++ ) { + for ( var i = 0, il = json.length; i < il; i ++ ) { - normalIndex = faces[ offset ++ ] * 3; + var image = json[ i ]; + var url = image.url; - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + if ( Array.isArray( url ) ) { - face.vertexNormals.push( normal ); + // load array of images e.g CubeTexture - } + images[ image.uuid ] = []; - } + for ( var j = 0, jl = url.length; j < jl; j ++ ) { + var currentUrl = url[ j ]; - if ( hasFaceColor ) { + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( currentUrl ) ? currentUrl : scope.resourcePath + currentUrl; - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); + images[ image.uuid ].push( loadImage( path ) ); } + } else { - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 3; i ++ ) { - - colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new Color( colors[ colorIndex ] ) ); - - } + // load single image - } + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.resourcePath + image.url; - geometry.faces.push( face ); + images[ image.uuid ] = loadImage( path ); } @@ -36493,1667 +38750,1760 @@ } - function parseSkin( json, geometry ) { + return images; - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + }, - if ( json.skinWeights ) { + parseTextures: function ( json, images ) { - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + function parseConstant( value, type ) { - var x = json.skinWeights[ i ]; - var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; - var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; - var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + if ( typeof value === 'number' ) return value; - geometry.skinWeights.push( new Vector4( x, y, z, w ) ); + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - } + return type[ value ]; - } + } - if ( json.skinIndices ) { + var textures = {}; - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + if ( json !== undefined ) { - var a = json.skinIndices[ i ]; - var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; - var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; - var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + for ( var i = 0, l = json.length; i < l; i ++ ) { - geometry.skinIndices.push( new Vector4( a, b, c, d ) ); + var data = json[ i ]; - } + if ( data.image === undefined ) { - } + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - geometry.bones = json.bones; + } - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + if ( images[ data.image ] === undefined ) { - console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - } + } - } + var texture; - function parseMorphing( json, geometry ) { + if ( Array.isArray( images[ data.image ] ) ) { - var scale = json.scale; + texture = new CubeTexture( images[ data.image ] ); - if ( json.morphTargets !== undefined ) { + } else { - for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + texture = new Texture( images[ data.image ] ); - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; + } - var dstVertices = geometry.morphTargets[ i ].vertices; - var srcVertices = json.morphTargets[ i ].vertices; + texture.needsUpdate = true; - for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + texture.uuid = data.uuid; - var vertex = new Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; + if ( data.name !== undefined ) texture.name = data.name; - dstVertices.push( vertex ); + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); - } + if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); + if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); + if ( data.center !== undefined ) texture.center.fromArray( data.center ); + if ( data.rotation !== undefined ) texture.rotation = data.rotation; - } + if ( data.wrap !== undefined ) { - } + texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); + texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); - if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { + } - console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); + if ( data.format !== undefined ) texture.format = data.format; + if ( data.type !== undefined ) texture.type = data.type; + if ( data.encoding !== undefined ) texture.encoding = data.encoding; - var faces = geometry.faces; - var morphColors = json.morphColors[ 0 ].colors; + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); + if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + if ( data.flipY !== undefined ) texture.flipY = data.flipY; - faces[ i ].color.fromArray( morphColors, i * 3 ); + if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; + if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; - } + textures[ data.uuid ] = texture; } } - function parseAnimations( json, geometry ) { - - var outputAnimations = []; - - // parse old style Bone/Hierarchy animations - var animations = []; + return textures; - if ( json.animation !== undefined ) { + }, - animations.push( json.animation ); + parseObject: function ( data, geometries, materials ) { - } + var object; - if ( json.animations !== undefined ) { + function getGeometry( name ) { - if ( json.animations.length ) { + if ( geometries[ name ] === undefined ) { - animations = animations.concat( json.animations ); + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); - } else { + } - animations.push( json.animations ); + return geometries[ name ]; - } + } - } + function getMaterial( name ) { - for ( var i = 0; i < animations.length; i ++ ) { + if ( name === undefined ) return undefined; - var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); - if ( clip ) outputAnimations.push( clip ); + if ( Array.isArray( name ) ) { - } + var array = []; - // parse implicit morph animations - if ( geometry.morphTargets ) { + for ( var i = 0, l = name.length; i < l; i ++ ) { - // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. - var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); - outputAnimations = outputAnimations.concat( morphAnimationClips ); + var uuid = name[ i ]; - } + if ( materials[ uuid ] === undefined ) { - if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; + console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); - } + } - return function parse( json, texturePath ) { + array.push( materials[ uuid ] ); - if ( json.data !== undefined ) { + } - // Geometry 4.0 spec - json = json.data; + return array; } - if ( json.scale !== undefined ) { - - json.scale = 1.0 / json.scale; - - } else { + if ( materials[ name ] === undefined ) { - json.scale = 1.0; + console.warn( 'THREE.ObjectLoader: Undefined material', name ); } - var geometry = new Geometry(); - - parseModel( json, geometry ); - parseSkin( json, geometry ); - parseMorphing( json, geometry ); - parseAnimations( json, geometry ); - - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); + return materials[ name ]; - if ( json.materials === undefined || json.materials.length === 0 ) { + } - return { geometry: geometry }; + switch ( data.type ) { - } else { + case 'Scene': - var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + object = new Scene(); - return { geometry: geometry, materials: materials }; + if ( data.background !== undefined ) { - } + if ( Number.isInteger( data.background ) ) { - }; + object.background = new Color( data.background ); - } )() + } - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( data.fog !== undefined ) { - function ObjectLoader( manager ) { + if ( data.fog.type === 'Fog' ) { - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.texturePath = ''; + object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - } + } else if ( data.fog.type === 'FogExp2' ) { - Object.assign( ObjectLoader.prototype, { + object.fog = new FogExp2( data.fog.color, data.fog.density ); - load: function ( url, onLoad, onProgress, onError ) { + } - if ( this.texturePath === '' ) { + } - this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); + break; - } + case 'PerspectiveCamera': - var scope = this; + object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - var loader = new FileLoader( scope.manager ); - loader.load( url, function ( text ) { + if ( data.focus !== undefined ) object.focus = data.focus; + if ( data.zoom !== undefined ) object.zoom = data.zoom; + if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; + if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; + if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - var json = null; + break; - try { + case 'OrthographicCamera': - json = JSON.parse( text ); + object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - } catch ( error ) { + if ( data.zoom !== undefined ) object.zoom = data.zoom; + if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - if ( onError !== undefined ) onError( error ); + break; - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); + case 'AmbientLight': - return; + object = new AmbientLight( data.color, data.intensity ); - } + break; - var metadata = json.metadata; + case 'DirectionalLight': - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { + object = new DirectionalLight( data.color, data.intensity ); - console.error( 'THREE.ObjectLoader: Can\'t load ' + url + '. Use THREE.JSONLoader instead.' ); - return; + break; - } + case 'PointLight': - scope.parse( json, onLoad ); + object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - }, onProgress, onError ); + break; - }, + case 'RectAreaLight': - setTexturePath: function ( value ) { + object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); - this.texturePath = value; + break; - }, + case 'SpotLight': - setCrossOrigin: function ( value ) { + object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - this.crossOrigin = value; + break; - }, + case 'HemisphereLight': - parse: function ( json, onLoad ) { + object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - var shapes = this.parseShape( json.shapes ); - var geometries = this.parseGeometries( json.geometries, shapes ); + break; - var images = this.parseImages( json.images, function () { + case 'SkinnedMesh': - if ( onLoad !== undefined ) onLoad( object ); + console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); - } ); + case 'Mesh': - var textures = this.parseTextures( json.textures, images ); - var materials = this.parseMaterials( json.materials, textures ); + var geometry = getGeometry( data.geometry ); + var material = getMaterial( data.material ); - var object = this.parseObject( json.object, geometries, materials ); + if ( geometry.bones && geometry.bones.length > 0 ) { - if ( json.animations ) { + object = new SkinnedMesh( geometry, material ); - object.animations = this.parseAnimations( json.animations ); + } else { - } + object = new Mesh( geometry, material ); - if ( json.images === undefined || json.images.length === 0 ) { + } - if ( onLoad !== undefined ) onLoad( object ); + if ( data.drawMode !== undefined ) object.setDrawMode( data.drawMode ); - } + break; - return object; + case 'LOD': - }, + object = new LOD(); - parseShape: function ( json ) { + break; - var shapes = {}; + case 'Line': - if ( json !== undefined ) { + object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - for ( var i = 0, l = json.length; i < l; i ++ ) { + break; - var shape = new Shape().fromJSON( json[ i ] ); + case 'LineLoop': - shapes[ shape.uuid ] = shape; + object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); - } + break; - } + case 'LineSegments': - return shapes; + object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - }, + break; - parseGeometries: function ( json, shapes ) { + case 'PointCloud': + case 'Points': - var geometries = {}; + object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - if ( json !== undefined ) { + break; - var geometryLoader = new JSONLoader(); - var bufferGeometryLoader = new BufferGeometryLoader(); + case 'Sprite': - for ( var i = 0, l = json.length; i < l; i ++ ) { + object = new Sprite( getMaterial( data.material ) ); - var geometry; - var data = json[ i ]; + break; - switch ( data.type ) { + case 'Group': - case 'PlaneGeometry': - case 'PlaneBufferGeometry': + object = new Group(); - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); + break; - break; + default: - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible + object = new Object3D(); - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); + } - break; + object.uuid = data.uuid; - case 'CircleGeometry': - case 'CircleBufferGeometry': + if ( data.name !== undefined ) object.name = data.name; - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); + if ( data.matrix !== undefined ) { - break; + object.matrix.fromArray( data.matrix ); - case 'CylinderGeometry': - case 'CylinderBufferGeometry': + if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; + if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); + } else { - break; + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - case 'ConeGeometry': - case 'ConeBufferGeometry': + } - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - break; + if ( data.shadow ) { - case 'SphereGeometry': - case 'SphereBufferGeometry': + if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; + if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; + if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); + if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); + } - break; + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; + if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; + if ( data.userData !== undefined ) object.userData = data.userData; + if ( data.layers !== undefined ) object.layers.mask = data.layers; - case 'DodecahedronGeometry': - case 'DodecahedronBufferGeometry': - case 'IcosahedronGeometry': - case 'IcosahedronBufferGeometry': - case 'OctahedronGeometry': - case 'OctahedronBufferGeometry': - case 'TetrahedronGeometry': - case 'TetrahedronBufferGeometry': + if ( data.children !== undefined ) { - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); + var children = data.children; - break; + for ( var i = 0; i < children.length; i ++ ) { - case 'RingGeometry': - case 'RingBufferGeometry': + object.add( this.parseObject( children[ i ], geometries, materials ) ); - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); + } - break; + } - case 'TorusGeometry': - case 'TorusBufferGeometry': + if ( data.type === 'LOD' ) { - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); + var levels = data.levels; - break; + for ( var l = 0; l < levels.length; l ++ ) { - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); + if ( child !== undefined ) { - break; + object.addLevel( child, level.distance ); - case 'LatheGeometry': - case 'LatheBufferGeometry': + } - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); + } - break; + } - case 'PolyhedronGeometry': - case 'PolyhedronBufferGeometry': + return object; - geometry = new Geometries[ data.type ]( - data.vertices, - data.indices, - data.radius, - data.details - ); + } - break; + } ); - case 'ShapeGeometry': - case 'ShapeBufferGeometry': + var TEXTURE_MAPPING = { + UVMapping: UVMapping, + CubeReflectionMapping: CubeReflectionMapping, + CubeRefractionMapping: CubeRefractionMapping, + EquirectangularReflectionMapping: EquirectangularReflectionMapping, + EquirectangularRefractionMapping: EquirectangularRefractionMapping, + SphericalReflectionMapping: SphericalReflectionMapping, + CubeUVReflectionMapping: CubeUVReflectionMapping, + CubeUVRefractionMapping: CubeUVRefractionMapping + }; - var geometryShapes = []; + var TEXTURE_WRAPPING = { + RepeatWrapping: RepeatWrapping, + ClampToEdgeWrapping: ClampToEdgeWrapping, + MirroredRepeatWrapping: MirroredRepeatWrapping + }; - for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { + var TEXTURE_FILTER = { + NearestFilter: NearestFilter, + NearestMipMapNearestFilter: NearestMipMapNearestFilter, + NearestMipMapLinearFilter: NearestMipMapLinearFilter, + LinearFilter: LinearFilter, + LinearMipMapNearestFilter: LinearMipMapNearestFilter, + LinearMipMapLinearFilter: LinearMipMapLinearFilter + }; - var shape = shapes[ data.shapes[ j ] ]; + /** + * @author thespite / http://clicktorelease.com/ + */ - geometryShapes.push( shape ); - } + function ImageBitmapLoader( manager ) { - geometry = new Geometries[ data.type ]( - geometryShapes, - data.curveSegments - ); + if ( typeof createImageBitmap === 'undefined' ) { - break; + console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - case 'BufferGeometry': + } - geometry = bufferGeometryLoader.parse( data ); + if ( typeof fetch === 'undefined' ) { - break; + console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - case 'Geometry': + } - geometry = geometryLoader.parse( data, this.texturePath ).geometry; + this.manager = manager !== undefined ? manager : DefaultLoadingManager; + this.options = undefined; - break; + } - default: + ImageBitmapLoader.prototype = { - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + constructor: ImageBitmapLoader, - continue; + setOptions: function setOptions( options ) { - } + this.options = options; - geometry.uuid = data.uuid; + return this; - if ( data.name !== undefined ) geometry.name = data.name; + }, - geometries[ data.uuid ] = geometry; + load: function ( url, onLoad, onProgress, onError ) { - } + if ( url === undefined ) url = ''; - } + if ( this.path !== undefined ) url = this.path + url; - return geometries; + url = this.manager.resolveURL( url ); - }, + var scope = this; - parseMaterials: function ( json, textures ) { + var cached = Cache.get( url ); - var materials = {}; + if ( cached !== undefined ) { - if ( json !== undefined ) { + scope.manager.itemStart( url ); - var loader = new MaterialLoader(); - loader.setTextures( textures ); + setTimeout( function () { - for ( var i = 0, l = json.length; i < l; i ++ ) { + if ( onLoad ) onLoad( cached ); - var data = json[ i ]; + scope.manager.itemEnd( url ); - if ( data.type === 'MultiMaterial' ) { + }, 0 ); - // Deprecated + return cached; - var array = []; + } - for ( var j = 0; j < data.materials.length; j ++ ) { + fetch( url ).then( function ( res ) { - array.push( loader.parse( data.materials[ j ] ) ); + return res.blob(); - } + } ).then( function ( blob ) { - materials[ data.uuid ] = array; + if ( scope.options === undefined ) { - } else { + // Workaround for FireFox. It causes an error if you pass options. + return createImageBitmap( blob ); - materials[ data.uuid ] = loader.parse( data ); + } else { - } + return createImageBitmap( blob, scope.options ); } - } - - return materials; + } ).then( function ( imageBitmap ) { - }, + Cache.add( url, imageBitmap ); - parseAnimations: function ( json ) { + if ( onLoad ) onLoad( imageBitmap ); - var animations = []; + scope.manager.itemEnd( url ); - for ( var i = 0; i < json.length; i ++ ) { + } ).catch( function ( e ) { - var clip = AnimationClip.parse( json[ i ] ); + if ( onError ) onError( e ); - animations.push( clip ); + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - } + } ); - return animations; + scope.manager.itemStart( url ); }, - parseImages: function ( json, onLoad ) { + setCrossOrigin: function ( /* value */ ) { - var scope = this; - var images = {}; + return this; - function loadImage( url ) { + }, - scope.manager.itemStart( url ); + setPath: function ( value ) { - return loader.load( url, function () { + this.path = value; + return this; - scope.manager.itemEnd( url ); + } - }, undefined, function () { + }; - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + /** + * @author zz85 / http://www.lab4games.net/zz85/blog + * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" + **/ - } ); + function ShapePath() { - } + this.type = 'ShapePath'; - if ( json !== undefined && json.length > 0 ) { + this.color = new Color(); - var manager = new LoadingManager( onLoad ); + this.subPaths = []; + this.currentPath = null; - var loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); + } - for ( var i = 0, l = json.length; i < l; i ++ ) { + Object.assign( ShapePath.prototype, { - var image = json[ i ]; - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; + moveTo: function ( x, y ) { - images[ image.uuid ] = loadImage( path ); + this.currentPath = new Path(); + this.subPaths.push( this.currentPath ); + this.currentPath.moveTo( x, y ); - } + }, - } + lineTo: function ( x, y ) { - return images; + this.currentPath.lineTo( x, y ); }, - parseTextures: function ( json, images ) { - - function parseConstant( value, type ) { + quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { - if ( typeof value === 'number' ) return value; + this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + }, - return type[ value ]; + bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - } + this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - var textures = {}; + }, - if ( json !== undefined ) { + splineThru: function ( pts ) { - for ( var i = 0, l = json.length; i < l; i ++ ) { + this.currentPath.splineThru( pts ); - var data = json[ i ]; + }, - if ( data.image === undefined ) { + toShapes: function ( isCCW, noHoles ) { - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); + function toShapesNoHoles( inSubpaths ) { - } + var shapes = []; - if ( images[ data.image ] === undefined ) { + for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + var tmpPath = inSubpaths[ i ]; - } + var tmpShape = new Shape(); + tmpShape.curves = tmpPath.curves; - var texture = new Texture( images[ data.image ] ); - texture.needsUpdate = true; + shapes.push( tmpShape ); - texture.uuid = data.uuid; + } - if ( data.name !== undefined ) texture.name = data.name; + return shapes; - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); + } - if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); - if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); - if ( data.center !== undefined ) texture.center.fromArray( data.center ); - if ( data.rotation !== undefined ) texture.rotation = data.rotation; + function isPointInsidePolygon( inPt, inPolygon ) { - if ( data.wrap !== undefined ) { + var polyLen = inPolygon.length; - texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - } + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; - if ( data.format !== undefined ) texture.format = data.format; + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; + if ( Math.abs( edgeDy ) > Number.EPSILON ) { - if ( data.flipY !== undefined ) texture.flipY = data.flipY; + // not parallel + if ( edgeDy < 0 ) { - textures[ data.uuid ] = texture; + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - } + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - } + if ( inPt.y === edgeLowPt.y ) { - return textures; + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! - }, + } else { - parseObject: function ( data, geometries, materials ) { + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt - var object; + } - function getGeometry( name ) { + } else { - if ( geometries[ name ] === undefined ) { + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + } } - return geometries[ name ]; + return inside; } - function getMaterial( name ) { + var isClockWise = ShapeUtils.isClockWise; - if ( name === undefined ) return undefined; + var subPaths = this.subPaths; + if ( subPaths.length === 0 ) return []; - if ( Array.isArray( name ) ) { + if ( noHoles === true ) return toShapesNoHoles( subPaths ); - var array = []; - for ( var i = 0, l = name.length; i < l; i ++ ) { + var solid, tmpPath, tmpShape, shapes = []; - var uuid = name[ i ]; + if ( subPaths.length === 1 ) { - if ( materials[ uuid ] === undefined ) { + tmpPath = subPaths[ 0 ]; + tmpShape = new Shape(); + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; - console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); + } - } + var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; - array.push( materials[ uuid ] ); + // console.log("Holes first", holesFirst); - } + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; - return array; + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; - } + for ( var i = 0, l = subPaths.length; i < l; i ++ ) { - if ( materials[ name ] === undefined ) { + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; - console.warn( 'THREE.ObjectLoader: Undefined material', name ); + if ( solid ) { - } + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - return materials[ name ]; + newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + + //console.log('ccw', i); + + } } - switch ( data.type ) { + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); - case 'Scene': - object = new Scene(); + if ( newShapes.length > 1 ) { - if ( data.background !== undefined ) { + var ambiguous = false; + var toChange = []; - if ( Number.isInteger( data.background ) ) { + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - object.background = new Color( data.background ); + betterShapeHoles[ sIdx ] = []; - } + } - } + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - if ( data.fog !== undefined ) { + var sho = newShapeHoles[ sIdx ]; - if ( data.fog.type === 'Fog' ) { + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); + var ho = sho[ hIdx ]; + var hole_unassigned = true; - } else if ( data.fog.type === 'FogExp2' ) { + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - object.fog = new FogExp2( data.fog.color, data.fog.density ); + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - } + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { - } + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); - break; + } else { - case 'PerspectiveCamera': + ambiguous = true; - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + } - if ( data.focus !== undefined ) object.focus = data.focus; - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; - if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); + } - break; + } + if ( hole_unassigned ) { - case 'OrthographicCamera': + betterShapeHoles[ sIdx ].push( ho ); - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + } - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); + } - break; + } + // console.log("ambiguous: ", ambiguous); + if ( toChange.length > 0 ) { - case 'AmbientLight': + // console.log("to change: ", toChange); + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; - object = new AmbientLight( data.color, data.intensity ); + } - break; + } - case 'DirectionalLight': + var tmpHoles; - object = new DirectionalLight( data.color, data.intensity ); + for ( var i = 0, il = newShapes.length; i < il; i ++ ) { - break; + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; - case 'PointLight': + for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); + tmpShape.holes.push( tmpHoles[ j ].h ); - break; + } - case 'RectAreaLight': + } - object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); + //console.log("shape", shapes); - break; + return shapes; - case 'SpotLight': + } - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); + } ); - break; + /** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author mrdoob / http://mrdoob.com/ + */ - case 'HemisphereLight': - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); + function Font( data ) { - break; + this.type = 'Font'; - case 'SkinnedMesh': + this.data = data; - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); + } - case 'Mesh': + Object.assign( Font.prototype, { - var geometry = getGeometry( data.geometry ); - var material = getMaterial( data.material ); + isFont: true, - if ( geometry.bones && geometry.bones.length > 0 ) { + generateShapes: function ( text, size ) { - object = new SkinnedMesh( geometry, material ); + if ( size === undefined ) size = 100; - } else { + var shapes = []; + var paths = createPaths( text, size, this.data ); - object = new Mesh( geometry, material ); + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - } + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; - break; + } - case 'LOD': + } ); - object = new LOD(); + function createPaths( text, size, data ) { - break; + var chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // see #13988 + var scale = size / data.resolution; + var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; - case 'Line': + var paths = []; - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); + var offsetX = 0, offsetY = 0; - break; + for ( var i = 0; i < chars.length; i ++ ) { - case 'LineLoop': + var char = chars[ i ]; - object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); + if ( char === '\n' ) { - break; + offsetX = 0; + offsetY -= line_height; - case 'LineSegments': + } else { - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); + var ret = createPath( char, scale, offsetX, offsetY, data ); + offsetX += ret.offsetX; + paths.push( ret.path ); - break; + } - case 'PointCloud': - case 'Points': + } - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); + return paths; - break; + } - case 'Sprite': + function createPath( char, scale, offsetX, offsetY, data ) { - object = new Sprite( getMaterial( data.material ) ); + var glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; - break; + if ( ! glyph ) return; - case 'Group': + var path = new ShapePath(); - object = new Group(); + var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; - break; + if ( glyph.o ) { - default: + var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - object = new Object3D(); + for ( var i = 0, l = outline.length; i < l; ) { - } + var action = outline[ i ++ ]; - object.uuid = data.uuid; + switch ( action ) { - if ( data.name !== undefined ) object.name = data.name; + case 'm': // moveTo - if ( data.matrix !== undefined ) { + x = outline[ i ++ ] * scale + offsetX; + y = outline[ i ++ ] * scale + offsetY; - object.matrix.fromArray( data.matrix ); + path.moveTo( x, y ); - if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; - if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); + break; - } else { + case 'l': // lineTo - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + x = outline[ i ++ ] * scale + offsetX; + y = outline[ i ++ ] * scale + offsetY; - } + path.lineTo( x, y ); - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; + break; - if ( data.shadow ) { + case 'q': // quadraticCurveTo - if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; - if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; - if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); - if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); + cpx = outline[ i ++ ] * scale + offsetX; + cpy = outline[ i ++ ] * scale + offsetY; + cpx1 = outline[ i ++ ] * scale + offsetX; + cpy1 = outline[ i ++ ] * scale + offsetY; - } + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; - if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; - if ( data.userData !== undefined ) object.userData = data.userData; + break; - if ( data.children !== undefined ) { + case 'b': // bezierCurveTo - var children = data.children; + cpx = outline[ i ++ ] * scale + offsetX; + cpy = outline[ i ++ ] * scale + offsetY; + cpx1 = outline[ i ++ ] * scale + offsetX; + cpy1 = outline[ i ++ ] * scale + offsetY; + cpx2 = outline[ i ++ ] * scale + offsetX; + cpy2 = outline[ i ++ ] * scale + offsetY; - for ( var i = 0; i < children.length; i ++ ) { + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - object.add( this.parseObject( children[ i ], geometries, materials ) ); + break; } } - if ( data.type === 'LOD' ) { - - var levels = data.levels; + } - for ( var l = 0; l < levels.length; l ++ ) { + return { offsetX: glyph.ha * scale, path: path }; - var level = levels[ l ]; - var child = object.getObjectByProperty( 'uuid', level.object ); + } - if ( child !== undefined ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - object.addLevel( child, level.distance ); + function FontLoader( manager ) { - } + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - } + } - } + Object.assign( FontLoader.prototype, { - return object; + load: function ( url, onLoad, onProgress, onError ) { - } + var scope = this; - } ); + var loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.load( url, function ( text ) { - var TEXTURE_MAPPING = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - SphericalReflectionMapping: SphericalReflectionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; + var json; - var TEXTURE_WRAPPING = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; + try { - var TEXTURE_FILTER = { - NearestFilter: NearestFilter, - NearestMipMapNearestFilter: NearestMipMapNearestFilter, - NearestMipMapLinearFilter: NearestMipMapLinearFilter, - LinearFilter: LinearFilter, - LinearMipMapNearestFilter: LinearMipMapNearestFilter, - LinearMipMapLinearFilter: LinearMipMapLinearFilter - }; + json = JSON.parse( text ); - /** - * @author thespite / http://clicktorelease.com/ - */ + } catch ( e ) { - function ImageBitmapLoader( manager ) { + console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); + json = JSON.parse( text.substring( 65, text.length - 2 ) ); - if ( typeof createImageBitmap === 'undefined' ) { + } - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); + var font = scope.parse( json ); - } + if ( onLoad ) onLoad( font ); - if ( typeof fetch === 'undefined' ) { + }, onProgress, onError ); - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + }, - } + parse: function ( json ) { - this.manager = manager !== undefined ? manager : DefaultLoadingManager; - this.options = undefined; + return new Font( json ); - } + }, - ImageBitmapLoader.prototype = { + setPath: function ( value ) { - constructor: ImageBitmapLoader, + this.path = value; + return this; - setOptions: function setOptions( options ) { + } - this.options = options; + } ); - return this; + /** + * @author alteredq / http://alteredqualia.com/ + */ - }, + function Loader() {} - load: function load( url, onLoad, onProgress, onError ) { + Loader.Handlers = { - if ( url === undefined ) url = ''; + handlers: [], - if ( this.path !== undefined ) url = this.path + url; + add: function ( regex, loader ) { - var scope = this; + this.handlers.push( regex, loader ); - var cached = Cache.get( url ); + }, - if ( cached !== undefined ) { + get: function ( file ) { - scope.manager.itemStart( url ); + var handlers = this.handlers; - setTimeout( function () { + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { - if ( onLoad ) onLoad( cached ); + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; - scope.manager.itemEnd( url ); + if ( regex.test( file ) ) { - }, 0 ); + return loader; - return cached; + } } - fetch( url ).then( function ( res ) { - - return res.blob(); + return null; - } ).then( function ( blob ) { + } - return createImageBitmap( blob, scope.options ); + }; - } ).then( function ( imageBitmap ) { + Object.assign( Loader.prototype, { - Cache.add( url, imageBitmap ); + crossOrigin: 'anonymous', - if ( onLoad ) onLoad( imageBitmap ); + onLoadStart: function () {}, - scope.manager.itemEnd( url ); + onLoadProgress: function () {}, - } ).catch( function ( e ) { + onLoadComplete: function () {}, - if ( onError ) onError( e ); + initMaterials: function ( materials, texturePath, crossOrigin ) { - scope.manager.itemEnd( url ); - scope.manager.itemError( url ); + var array = []; - } ); + for ( var i = 0; i < materials.length; ++ i ) { - }, + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); - setCrossOrigin: function ( /* value */ ) { + } - return this; + return array; }, - setPath: function ( value ) { - - this.path = value; - return this; - - } + createMaterial: ( function () { - }; + var BlendingMode = { + NoBlending: NoBlending, + NormalBlending: NormalBlending, + AdditiveBlending: AdditiveBlending, + SubtractiveBlending: SubtractiveBlending, + MultiplyBlending: MultiplyBlending, + CustomBlending: CustomBlending + }; - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" - **/ + var color = new Color(); + var textureLoader = new TextureLoader(); + var materialLoader = new MaterialLoader(); - function ShapePath() { + return function createMaterial( m, texturePath, crossOrigin ) { - this.type = 'ShapePath'; + // convert from old material format - this.color = new Color(); + var textures = {}; - this.subPaths = []; - this.currentPath = null; + function loadTexture( path, repeat, offset, wrap, anisotropy ) { - } + var fullPath = texturePath + path; + var loader = Loader.Handlers.get( fullPath ); - Object.assign( ShapePath.prototype, { + var texture; - moveTo: function ( x, y ) { + if ( loader !== null ) { - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); + texture = loader.load( fullPath ); - }, + } else { - lineTo: function ( x, y ) { + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); - this.currentPath.lineTo( x, y ); + } - }, + if ( repeat !== undefined ) { - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + texture.repeat.fromArray( repeat ); - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); + if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; - }, + } - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + if ( offset !== undefined ) { - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); + texture.offset.fromArray( offset ); - }, + } - splineThru: function ( pts ) { + if ( wrap !== undefined ) { - this.currentPath.splineThru( pts ); + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; - }, + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; - toShapes: function ( isCCW, noHoles ) { + } - function toShapesNoHoles( inSubpaths ) { + if ( anisotropy !== undefined ) { - var shapes = []; + texture.anisotropy = anisotropy; - for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { + } - var tmpPath = inSubpaths[ i ]; + var uuid = _Math.generateUUID(); - var tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; + textures[ uuid ] = texture; - shapes.push( tmpShape ); + return uuid; } - return shapes; + // - } + var json = { + uuid: _Math.generateUUID(), + type: 'MeshLambertMaterial' + }; - function isPointInsidePolygon( inPt, inPolygon ) { + for ( var name in m ) { - var polyLen = inPolygon.length; + var value = m[ name ]; - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + switch ( name ) { - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; + case 'DbgColor': + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = BlendingMode[ value ]; + break; + case 'colorAmbient': + case 'mapAmbient': + console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); + break; + case 'colorDiffuse': + json.color = color.fromArray( value ).getHex(); + break; + case 'colorSpecular': + json.specular = color.fromArray( value ).getHex(); + break; + case 'colorEmissive': + json.emissive = color.fromArray( value ).getHex(); + break; + case 'specularCoef': + json.shininess = value; + break; + case 'shading': + if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; + if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; + if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; + break; + case 'mapDiffuse': + json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + break; + case 'mapDiffuseRepeat': + case 'mapDiffuseOffset': + case 'mapDiffuseWrap': + case 'mapDiffuseAnisotropy': + break; + case 'mapEmissive': + json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); + break; + case 'mapEmissiveRepeat': + case 'mapEmissiveOffset': + case 'mapEmissiveWrap': + case 'mapEmissiveAnisotropy': + break; + case 'mapLight': + json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + break; + case 'mapLightRepeat': + case 'mapLightOffset': + case 'mapLightWrap': + case 'mapLightAnisotropy': + break; + case 'mapAO': + json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + break; + case 'mapAORepeat': + case 'mapAOOffset': + case 'mapAOWrap': + case 'mapAOAnisotropy': + break; + case 'mapBump': + json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + break; + case 'mapBumpScale': + json.bumpScale = value; + break; + case 'mapBumpRepeat': + case 'mapBumpOffset': + case 'mapBumpWrap': + case 'mapBumpAnisotropy': + break; + case 'mapNormal': + json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + break; + case 'mapNormalFactor': + json.normalScale = value; + break; + case 'mapNormalRepeat': + case 'mapNormalOffset': + case 'mapNormalWrap': + case 'mapNormalAnisotropy': + break; + case 'mapSpecular': + json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + break; + case 'mapSpecularRepeat': + case 'mapSpecularOffset': + case 'mapSpecularWrap': + case 'mapSpecularAnisotropy': + break; + case 'mapMetalness': + json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); + break; + case 'mapMetalnessRepeat': + case 'mapMetalnessOffset': + case 'mapMetalnessWrap': + case 'mapMetalnessAnisotropy': + break; + case 'mapRoughness': + json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); + break; + case 'mapRoughnessRepeat': + case 'mapRoughnessOffset': + case 'mapRoughnessWrap': + case 'mapRoughnessAnisotropy': + break; + case 'mapAlpha': + json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + break; + case 'mapAlphaRepeat': + case 'mapAlphaOffset': + case 'mapAlphaWrap': + case 'mapAlphaAnisotropy': + break; + case 'flipSided': + json.side = BackSide; + break; + case 'doubleSided': + json.side = DoubleSide; + break; + case 'transparency': + console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); + json.opacity = value; + break; + case 'depthTest': + case 'depthWrite': + case 'colorWrite': + case 'opacity': + case 'reflectivity': + case 'transparent': + case 'visible': + case 'wireframe': + json[ name ] = value; + break; + case 'vertexColors': + if ( value === true ) json.vertexColors = VertexColors; + if ( value === 'face' ) json.vertexColors = FaceColors; + break; + default: + console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); + break; - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; + } - if ( Math.abs( edgeDy ) > Number.EPSILON ) { + } - // not parallel - if ( edgeDy < 0 ) { + if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + if ( json.opacity < 1 ) json.transparent = true; - } - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + materialLoader.setTextures( textures ); - if ( inPt.y === edgeLowPt.y ) { + return materialLoader.parse( json ); - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! + }; - } else { + } )() - var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + */ - } else { + var context; - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; + var AudioContext = { - } + getContext: function () { - } + if ( context === undefined ) { - return inside; + context = new ( window.AudioContext || window.webkitAudioContext )(); } - var isClockWise = ShapeUtils.isClockWise; - - var subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; + return context; - if ( noHoles === true ) return toShapesNoHoles( subPaths ); + }, + setContext: function ( value ) { - var solid, tmpPath, tmpShape, shapes = []; + context = value; - if ( subPaths.length === 1 ) { + } - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + }; - } + /** + * @author Reece Aaron Lecrivain / http://reecenotes.com/ + */ - var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; + function AudioLoader( manager ) { - // console.log("Holes first", holesFirst); + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; + } - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; + Object.assign( AudioLoader.prototype, { - for ( var i = 0, l = subPaths.length; i < l; i ++ ) { + load: function ( url, onLoad, onProgress, onError ) { - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; + var loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + loader.load( url, function ( buffer ) { - if ( solid ) { + // Create a copy of the buffer. The `decodeAudioData` method + // detaches the buffer when complete, preventing reuse. + var bufferCopy = buffer.slice( 0 ); - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + var context = AudioContext.getContext(); + context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; + onLoad( audioBuffer ); - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; + } ); - //console.log('cw', i); + }, onProgress, onError ); - } else { + }, - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + setPath: function ( value ) { - //console.log('ccw', i); + this.path = value; + return this; - } + } - } + } ); - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + /** + * @author bhouston / http://clara.io + * @author WestLangley / http://github.com/WestLangley + * + * 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 - if ( newShapes.length > 1 ) { + function SphericalHarmonics3() { - var ambiguous = false; - var toChange = []; + this.coefficients = []; - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + for ( var i = 0; i < 9; i ++ ) { - betterShapeHoles[ sIdx ] = []; + this.coefficients.push( new Vector3() ); - } + } - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + } - var sho = newShapeHoles[ sIdx ]; + Object.assign( SphericalHarmonics3.prototype, { - for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + isSphericalHarmonics3: true, - var ho = sho[ hIdx ]; - var hole_unassigned = true; + set: function ( coefficients ) { - for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + for ( var i = 0; i < 9; i ++ ) { - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + this.coefficients[ i ].copy( coefficients[ i ] ); - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { + } - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); + return this; - } else { + }, - ambiguous = true; + zero: function () { - } + for ( var i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].set( 0, 0, 0 ); - } - if ( hole_unassigned ) { + } - betterShapeHoles[ sIdx ].push( ho ); + return this; - } + }, - } + // get the radiance in the direction of the normal + // target is a Vector3 + getAt: function ( normal, target ) { - } - // console.log("ambiguous: ", ambiguous); - if ( toChange.length > 0 ) { + // normal is assumed to be unit length - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + var x = normal.x, y = normal.y, z = normal.z; - } + var coeff = this.coefficients; - } + // band 0 + target = coeff[ 0 ] * 0.282095; - var tmpHoles; + // band 1 + target += coeff[ 1 ] * 0.488603 * y; + target += coeff[ 2 ] * 0.488603 * z; + target += coeff[ 3 ] * 0.488603 * x; - for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + // band 2 + target += coeff[ 4 ] * 1.092548 * ( x * y ); + target += coeff[ 5 ] * 1.092548 * ( y * z ); + target += coeff[ 6 ] * 0.315392 * ( 3.0 * z * z - 1.0 ); + target += coeff[ 7 ] * 1.092548 * ( x * z ); + target += coeff[ 8 ] * 0.546274 * ( x * x - y * y ); - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; + return target; - for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + }, - tmpShape.holes.push( tmpHoles[ j ].h ); + // 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: function ( normal, target ) { - } + // normal is assumed to be unit length - } + var x = normal.x, y = normal.y, z = normal.z; - //console.log("shape", shapes); + var coeff = this.coefficients; - return shapes; + // band 0 + target = coeff[ 0 ] * 0.886227; // π * 0.282095 - } + // band 1 + target += coeff[ 1 ] * 2.0 * 0.511664 * y; // ( 2 * π / 3 ) * 0.488603 + target += coeff[ 2 ] * 2.0 * 0.511664 * z; + target += coeff[ 3 ] * 2.0 * 0.511664 * x; - } ); + // band 2 + target += coeff[ 4 ] * 2.0 * 0.429043 * x * y; // ( π / 4 ) * 1.092548 + target += coeff[ 5 ] * 2.0 * 0.429043 * y * z; + target += coeff[ 6 ] * ( 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 + target += coeff[ 7 ] * 2.0 * 0.429043 * x * z; + target += coeff[ 8 ] * 0.429043 * ( x * x - y * y ); // ( π / 4 ) * 0.546274 - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author mrdoob / http://mrdoob.com/ - */ + return target; - function Font( data ) { + }, - this.type = 'Font'; + add: function ( sh ) { - this.data = data; + for ( var i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].add( sh.coefficients[ i ] ); - Object.assign( Font.prototype, { + } - isFont: true, + return this; - generateShapes: function ( text, size, divisions ) { + }, - if ( size === undefined ) size = 100; - if ( divisions === undefined ) divisions = 4; - var shapes = []; - var paths = createPaths( text, size, divisions, this.data ); + scale: function ( s ) { - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + for ( var i = 0; i < 9; i ++ ) { - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + this.coefficients[ i ].multiplyScalar( s ); } - return shapes; + return this; - } + }, - } ); + lerp: function ( sh, alpha ) { - function createPaths( text, size, divisions, data ) { + for ( var i = 0; i < 9; i ++ ) { - var chars = String( text ).split( '' ); - var scale = size / data.resolution; - var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; + this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - var paths = []; + } - var offsetX = 0, offsetY = 0; + return this; - for ( var i = 0; i < chars.length; i ++ ) { + }, - var char = chars[ i ]; + equals: function ( sh ) { - if ( char === '\n' ) { + for ( var i = 0; i < 9; i ++ ) { - offsetX = 0; - offsetY -= line_height; + if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - } else { + return false; - var ret = createPath( char, divisions, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); + } } - } - - return paths; + return true; - } + }, - function createPath( char, divisions, scale, offsetX, offsetY, data ) { + copy: function ( sh ) { - var glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; + return this.set( sh.coefficients ); - if ( ! glyph ) return; + }, - var path = new ShapePath(); + clone: function () { - var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; + return new this.constructor().copy( this ); - if ( glyph.o ) { + }, - var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + fromArray: function ( array ) { - for ( var i = 0, l = outline.length; i < l; ) { + var coefficients = this.coefficients; - var action = outline[ i ++ ]; + for ( var i = 0; i < 9; i ++ ) { - switch ( action ) { + coefficients[ i ].fromArray( array, i * 3 ); - case 'm': // moveTo + } - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + return this; - path.moveTo( x, y ); + }, - break; + toArray: function () { - case 'l': // lineTo + var array = []; + var coefficients = this.coefficients; - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + for ( var i = 0; i < 9; i ++ ) { - path.lineTo( x, y ); + coefficients[ i ].toArray( array, i * 3 ); - break; + } - case 'q': // quadraticCurveTo + return array; - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; + } - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + } ); - break; + Object.assign( SphericalHarmonics3, { - case 'b': // bezierCurveTo + // evaluate the basis functions + // shBasis is an Array[ 9 ] + getBasisAt: function ( normal, shBasis ) { - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; + // normal is assumed to be unit length - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + var x = normal.x, y = normal.y, z = normal.z; - break; + // band 0 + shBasis[ 0 ] = 0.282095; - } + // band 1 + shBasis[ 1 ] = 0.488603 * y; + shBasis[ 2 ] = 0.488603 * z; + shBasis[ 3 ] = 0.488603 * x; - } + // 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 { offsetX: glyph.ha * scale, path: path }; - - } + } ); /** - * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * + * A LightProbe is a source of indirect-diffuse light */ - function FontLoader( manager ) { + function LightProbe( sh, intensity ) { - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + Light.call( this, undefined, intensity ); - } + this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); - Object.assign( FontLoader.prototype, { + } - load: function ( url, onLoad, onProgress, onError ) { + LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { - var scope = this; + constructor: LightProbe, - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.load( url, function ( text ) { + isLightProbe: true, - var json; + copy: function ( source ) { - try { + Light.prototype.copy.call( this, source ); - json = JSON.parse( text ); + this.sh.copy( source.sh ); + this.intensity = source.intensity; - } catch ( e ) { + return this; - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); + }, - } + toJSON: function ( meta ) { - var font = scope.parse( json ); + var data = Light.prototype.toJSON.call( this, meta ); - if ( onLoad ) onLoad( font ); + // data.sh = this.sh.toArray(); // todo - }, onProgress, onError ); + return data; - }, + } - parse: function ( json ) { + } ); - return new Font( json ); + /** + * @author WestLangley / http://github.com/WestLangley + */ - }, + function HemisphereLightProbe( skyColor, groundColor, intensity ) { - setPath: function ( value ) { + LightProbe.call( this, undefined, intensity ); - this.path = value; - return this; + var color1 = new Color().set( skyColor ); + var color2 = new Color().set( groundColor ); - } + var sky = new Vector3( color1.r, color1.g, color1.b ); + var ground = new Vector3( color2.r, color2.g, color2.b ); - } ); + // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); + var c0 = Math.sqrt( Math.PI ); + var c1 = c0 * Math.sqrt( 0.75 ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); + this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); - var context; + } - var AudioContext = { + HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - getContext: function () { + constructor: HemisphereLightProbe, - if ( context === undefined ) { + isHemisphereLightProbe: true, - context = new ( window.AudioContext || window.webkitAudioContext )(); + copy: function ( source ) { // modifying colors not currently supported - } + LightProbe.prototype.copy.call( this, source ); - return context; + return this; }, - setContext: function ( value ) { + toJSON: function ( meta ) { - context = value; + var data = LightProbe.prototype.toJSON.call( this, meta ); + + // data.sh = this.sh.toArray(); // todo + + return data; } - }; + } ); /** - * @author Reece Aaron Lecrivain / http://reecenotes.com/ + * @author WestLangley / http://github.com/WestLangley */ - function AudioLoader( manager ) { + function AmbientLightProbe( color, intensity ) { - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + LightProbe.call( this, undefined, intensity ); + + var color1 = new Color().set( color ); + + // 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 ) ); } - Object.assign( AudioLoader.prototype, { + AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - load: function ( url, onLoad, onProgress, onError ) { + constructor: AmbientLightProbe, - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.load( url, function ( buffer ) { + isAmbientLightProbe: true, - var context = AudioContext.getContext(); + copy: function ( source ) { // modifying color not currently supported - context.decodeAudioData( buffer, function ( audioBuffer ) { + LightProbe.prototype.copy.call( this, source ); - onLoad( audioBuffer ); + return this; - } ); + }, - }, onProgress, onError ); + toJSON: function ( meta ) { + + var data = LightProbe.prototype.toJSON.call( this, meta ); + + // data.sh = this.sh.toArray(); // todo + + return data; } @@ -38258,14 +40608,14 @@ * @author alteredq / http://alteredqualia.com/ */ - function CubeCamera( near, far, cubeResolution ) { + var fov = 90, aspect = 1; + + function CubeCamera( near, far, cubeResolution, options ) { Object3D.call( this ); this.type = 'CubeCamera'; - var fov = 90, aspect = 1; - var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); @@ -38296,7 +40646,7 @@ cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); - var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; + options = options || { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.renderTarget.texture.name = "CubeCamera"; @@ -38305,49 +40655,52 @@ if ( this.parent === null ) this.updateMatrixWorld(); + var currentRenderTarget = renderer.getRenderTarget(); + var renderTarget = this.renderTarget; var generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; - renderTarget.activeCubeFace = 0; - renderer.render( scene, cameraPX, renderTarget ); + renderer.setRenderTarget( renderTarget, 0 ); + renderer.render( scene, cameraPX ); - renderTarget.activeCubeFace = 1; - renderer.render( scene, cameraNX, renderTarget ); + renderer.setRenderTarget( renderTarget, 1 ); + renderer.render( scene, cameraNX ); - renderTarget.activeCubeFace = 2; - renderer.render( scene, cameraPY, renderTarget ); + renderer.setRenderTarget( renderTarget, 2 ); + renderer.render( scene, cameraPY ); - renderTarget.activeCubeFace = 3; - renderer.render( scene, cameraNY, renderTarget ); + renderer.setRenderTarget( renderTarget, 3 ); + renderer.render( scene, cameraNY ); - renderTarget.activeCubeFace = 4; - renderer.render( scene, cameraPZ, renderTarget ); + renderer.setRenderTarget( renderTarget, 4 ); + renderer.render( scene, cameraPZ ); renderTarget.texture.generateMipmaps = generateMipmaps; - renderTarget.activeCubeFace = 5; - renderer.render( scene, cameraNZ, renderTarget ); + renderer.setRenderTarget( renderTarget, 5 ); + renderer.render( scene, cameraNZ ); - renderer.setRenderTarget( null ); + renderer.setRenderTarget( currentRenderTarget ); }; this.clear = function ( renderer, color, depth, stencil ) { + var currentRenderTarget = renderer.getRenderTarget(); + var renderTarget = this.renderTarget; for ( var i = 0; i < 6; i ++ ) { - renderTarget.activeCubeFace = i; - renderer.setRenderTarget( renderTarget ); + renderer.setRenderTarget( renderTarget, i ); renderer.clear( color, depth, stencil ); } - renderer.setRenderTarget( null ); + renderer.setRenderTarget( currentRenderTarget ); }; @@ -38356,6 +40709,77 @@ CubeCamera.prototype = Object.create( Object3D.prototype ); CubeCamera.prototype.constructor = CubeCamera; + /** + * @author alteredq / http://alteredqualia.com/ + */ + + function Clock( autoStart ) { + + this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + + this.running = false; + + } + + Object.assign( Clock.prototype, { + + start: function () { + + this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 + + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; + + }, + + stop: function () { + + this.getElapsedTime(); + this.running = false; + this.autoStart = false; + + }, + + getElapsedTime: function () { + + this.getDelta(); + return this.elapsedTime; + + }, + + getDelta: function () { + + var diff = 0; + + if ( this.autoStart && ! this.running ) { + + this.start(); + return 0; + + } + + if ( this.running ) { + + var newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); + + diff = ( newTime - this.oldTime ) / 1000; + this.oldTime = newTime; + + this.elapsedTime += diff; + + } + + return diff; + + } + + } ); + /** * @author mrdoob / http://mrdoob.com/ */ @@ -38373,6 +40797,8 @@ this.filter = null; + this.timeDelta = 0; + } AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { @@ -38396,6 +40822,8 @@ } + return this; + }, getFilter: function () { @@ -38421,6 +40849,8 @@ this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); + return this; + }, getMasterVolume: function () { @@ -38433,6 +40863,8 @@ this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); + return this; + }, updateMatrixWorld: ( function () { @@ -38442,6 +40874,7 @@ var scale = new Vector3(); var orientation = new Vector3(); + var clock = new Clock(); return function updateMatrixWorld( force ) { @@ -38450,21 +40883,27 @@ var listener = this.context.listener; var up = this.up; + this.timeDelta = clock.getDelta(); + this.matrixWorld.decompose( position, quaternion, scale ); orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); if ( listener.positionX ) { - listener.positionX.setValueAtTime( position.x, this.context.currentTime ); - listener.positionY.setValueAtTime( position.y, this.context.currentTime ); - listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); - listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); - listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); - listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); - listener.upX.setValueAtTime( up.x, this.context.currentTime ); - listener.upY.setValueAtTime( up.y, this.context.currentTime ); - listener.upZ.setValueAtTime( up.z, this.context.currentTime ); + // code path for Chrome (see #14393) + + var endTime = this.context.currentTime + this.timeDelta; + + listener.positionX.linearRampToValueAtTime( position.x, endTime ); + listener.positionY.linearRampToValueAtTime( position.y, endTime ); + listener.positionZ.linearRampToValueAtTime( position.z, endTime ); + listener.forwardX.linearRampToValueAtTime( orientation.x, endTime ); + listener.forwardY.linearRampToValueAtTime( orientation.y, endTime ); + listener.forwardZ.linearRampToValueAtTime( orientation.z, endTime ); + listener.upX.linearRampToValueAtTime( up.x, endTime ); + listener.upY.linearRampToValueAtTime( up.y, endTime ); + listener.upZ.linearRampToValueAtTime( up.z, endTime ); } else { @@ -38490,6 +40929,7 @@ this.type = 'Audio'; + this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); @@ -38498,6 +40938,7 @@ this.autoplay = false; this.buffer = null; + this.detune = 0; this.loop = false; this.startTime = 0; this.offset = 0; @@ -38531,6 +40972,17 @@ }, + setMediaElementSource: function ( mediaElement ) { + + this.hasPlaybackControl = false; + this.sourceType = 'mediaNode'; + this.source = this.context.createMediaElementSource( mediaElement ); + this.connect(); + + return this; + + }, + setBuffer: function ( audioBuffer ) { this.buffer = audioBuffer; @@ -38563,7 +41015,6 @@ source.buffer = this.buffer; source.loop = this.loop; source.onended = this.onEnded.bind( this ); - source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); this.startTime = this.context.currentTime; source.start( this.startTime, this.offset ); @@ -38571,6 +41022,9 @@ this.source = source; + this.setDetune( this.detune ); + this.setPlaybackRate( this.playbackRate ); + return this.connect(); }, @@ -38587,6 +41041,7 @@ if ( this.isPlaying === true ) { this.source.stop(); + this.source.onended = null; this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; this.isPlaying = false; @@ -38606,6 +41061,7 @@ } this.source.stop(); + this.source.onended = null; this.offset = 0; this.isPlaying = false; @@ -38687,6 +41143,28 @@ }, + setDetune: function ( value ) { + + this.detune = value; + + if ( this.source.detune === undefined ) return; // only set detune when available + + if ( this.isPlaying === true ) { + + this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); + + } + + return this; + + }, + + getDetune: function () { + + return this.detune; + + }, + getFilter: function () { return this.getFilters()[ 0 ]; @@ -38712,7 +41190,7 @@ if ( this.isPlaying === true ) { - this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); + this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); } @@ -38815,6 +41293,8 @@ this.panner.refDistance = value; + return this; + }, getRolloffFactor: function () { @@ -38827,6 +41307,8 @@ this.panner.rolloffFactor = value; + return this; + }, getDistanceModel: function () { @@ -38839,6 +41321,8 @@ this.panner.distanceModel = value; + return this; + }, getMaxDistance: function () { @@ -38851,19 +41335,59 @@ this.panner.maxDistance = value; + return this; + + }, + + setDirectionalCone: function ( coneInnerAngle, coneOuterAngle, coneOuterGain ) { + + this.panner.coneInnerAngle = coneInnerAngle; + this.panner.coneOuterAngle = coneOuterAngle; + this.panner.coneOuterGain = coneOuterGain; + + return this; + }, updateMatrixWorld: ( function () { var position = new Vector3(); + var quaternion = new Quaternion(); + var scale = new Vector3(); + + var orientation = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); - position.setFromMatrixPosition( this.matrixWorld ); + if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, 1 ).applyQuaternion( quaternion ); + + var panner = this.panner; - this.panner.setPosition( position.x, position.y, position.z ); + if ( panner.positionX ) { + + // code path for Chrome and Firefox (see #14393) + + var endTime = this.context.currentTime + this.listener.timeDelta; + + panner.positionX.linearRampToValueAtTime( position.x, endTime ); + panner.positionY.linearRampToValueAtTime( position.y, endTime ); + panner.positionZ.linearRampToValueAtTime( position.z, endTime ); + panner.orientationX.linearRampToValueAtTime( orientation.x, endTime ); + panner.orientationY.linearRampToValueAtTime( orientation.y, endTime ); + panner.orientationZ.linearRampToValueAtTime( orientation.z, endTime ); + + } else { + + panner.setPosition( position.x, position.y, position.z ); + panner.setOrientation( orientation.x, orientation.y, orientation.z ); + + } }; @@ -39158,8 +41682,7 @@ var bindings = this._bindings; - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { + for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); @@ -39171,8 +41694,7 @@ var bindings = this._bindings; - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { + for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); @@ -39184,8 +41706,7 @@ var bindings = this._bindings; - for ( var i = this._targetGroup.nCachedObjects_, - n = bindings.length; i !== n; ++ i ) { + for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); @@ -39229,7 +41750,7 @@ * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * - * @param {string} name Node name to be sanitized. + * @param {string} name Node name to be sanitized. * @return {string} */ sanitizeNodeName: ( function () { @@ -39714,15 +42235,15 @@ // determine versioning scheme var versioning = this.Versioning.None; + this.targetObject = targetObject; + if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; - this.targetObject = targetObject; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; - this.targetObject = targetObject; } @@ -39849,27 +42370,27 @@ * * Usage: * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. + * - Add objects you would otherwise pass as 'root' to the + * constructor or the .clipAction method of AnimationMixer. * - * - Instead pass this object as 'root'. + * - Instead pass this object as 'root'. * - * - You can also add and remove objects later when the mixer - * is running. + * - You can also add and remove objects later when the mixer + * is running. * * Note: * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. + * Objects of this class appear as one object to the mixer, + * so cache control of the individual objects must be done + * on the group. * * Limitation: * - * - The animated properties must be compatible among the - * all objects in the group. + * - The animated properties must be compatible among the + * all objects in the group. * - * - A single property can either be controlled through a - * target group or directly, but not both. + * - A single property can either be controlled through a + * target group or directly, but not both. * * @author tschw */ @@ -39881,11 +42402,11 @@ // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); - this.nCachedObjects_ = 0; // threshold + this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite var indices = {}; - this._indicesByUUID = indices; // for bookkeeping + this._indicesByUUID = indices; // for bookkeeping for ( var i = 0, n = arguments.length; i !== n; ++ i ) { @@ -39893,10 +42414,10 @@ } - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays + this._paths = []; // inside: string + this._parsedPaths = []; // inside: { we don't care, here } + this._bindings = []; // inside: Array< PropertyBinding > + this._bindingsIndicesByPath = {}; // inside: indices in these arrays var scope = this; @@ -40004,7 +42525,7 @@ } else if ( objects[ index ] !== knownObject ) { console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); + 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); } // else the object is already where we want it to be @@ -40255,13 +42776,13 @@ this._interpolantSettings = interpolantSettings; - this._interpolants = interpolants; // bound by the mixer + 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 + this._cacheIndex = null; // for the memory manager + this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; @@ -40283,15 +42804,15 @@ this.weight = 1; this._effectiveWeight = 1; - this.repetitions = Infinity; // no. of repetitions when looping + this.repetitions = Infinity; // no. of repetitions when looping - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight + this.paused = false; // true -> zero effective time scale + this.enabled = true; // false -> zero effective weight - this.clampWhenFinished = false; // keep feeding the last frame? + 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 + this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate + this.zeroSlopeAtEnd = true;// clips for start, loop and end } @@ -40320,9 +42841,9 @@ this.paused = false; this.enabled = true; - this.time = 0; // restart clip - this._loopCount = - 1; // forget previous loops - this._startTime = null; // forget scheduling + this.time = 0; // restart clip + this._loopCount = - 1;// forget previous loops + this._startTime = null;// forget scheduling return this.stopFading().stopWarping(); @@ -40331,7 +42852,7 @@ isRunning: function () { return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); + this._startTime === null && this._mixer._isActiveAction( this ); }, @@ -40687,13 +43208,19 @@ _updateTime: function ( deltaTime ) { var time = this.time + deltaTime; + var duration = this._clip.duration; + var loop = this.loop; + var loopCount = this._loopCount; + + var pingPong = ( loop === LoopPingPong ); - if ( deltaTime === 0 ) return time; + if ( deltaTime === 0 ) { - var duration = this._clip.duration, + if ( loopCount === - 1 ) return time; - loop = this.loop, - loopCount = this._loopCount; + return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; + + } if ( loop === LoopOnce ) { @@ -40730,8 +43257,6 @@ } else { // repetitive Repeat or PingPong - var pingPong = ( loop === LoopPingPong ); - if ( loopCount === - 1 ) { // just started @@ -40828,8 +43353,8 @@ if ( pingPong ) { - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; + settings.endingStart = ZeroSlopeEnding; + settings.endingEnd = ZeroSlopeEnding; } else { @@ -40874,8 +43399,10 @@ var times = interpolant.parameterPositions, values = interpolant.sampleValues; - times[ 0 ] = now; values[ 0 ] = weightNow; - times[ 1 ] = now + duration; values[ 1 ] = weightThen; + times[ 0 ] = now; + values[ 0 ] = weightNow; + times[ 1 ] = now + duration; + values[ 1 ] = weightThen; return this; @@ -41054,8 +43581,8 @@ this._actionsByClip = {}; // inside: // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup + // knownActions: Array< AnimationAction > - used as prototypes + // actionByRoot: AnimationAction - lookup // } @@ -41695,250 +44222,6 @@ } ); - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { - - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; - - this.normalized = normalized === true; - - } - - Object.defineProperties( InterleavedBufferAttribute.prototype, { - - count: { - - get: function () { - - return this.data.count; - - } - - }, - - array: { - - get: function () { - - return this.data.array; - - } - - } - - } ); - - Object.assign( InterleavedBufferAttribute.prototype, { - - isInterleavedBufferAttribute: true, - - setX: function ( index, x ) { - - this.data.array[ index * this.data.stride + this.offset ] = x; - - return this; - - }, - - setY: function ( index, y ) { - - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - - return this; - - }, - - setZ: function ( index, z ) { - - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - - return this; - - }, - - setW: function ( index, w ) { - - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - - return this; - - }, - - getX: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset ]; - - }, - - getY: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 1 ]; - - }, - - getZ: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 2 ]; - - }, - - getW: function ( index ) { - - return this.data.array[ index * this.data.stride + this.offset + 3 ]; - - }, - - setXY: function ( index, x, y ) { - - index = index * this.data.stride + this.offset; - - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - - return this; - - }, - - setXYZ: function ( index, x, y, z ) { - - 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; - - return this; - - }, - - setXYZW: function ( index, x, y, z, w ) { - - 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; - this.data.array[ index + 3 ] = w; - - return this; - - } - - } ); - - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ - - function InterleavedBuffer( array, stride ) { - - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; - - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; - - this.version = 0; - - } - - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { - - set: function ( value ) { - - if ( value === true ) this.version ++; - - } - - } ); - - Object.assign( InterleavedBuffer.prototype, { - - isInterleavedBuffer: true, - - onUploadCallback: function () {}, - - setArray: function ( array ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.count = array !== undefined ? array.length / this.stride : 0; - this.array = array; - - return this; - - }, - - setDynamic: function ( value ) { - - this.dynamic = value; - - return this; - - }, - - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.dynamic = source.dynamic; - - return this; - - }, - - copyAt: function ( index1, attribute, index2 ) { - - index1 *= this.stride; - index2 *= attribute.stride; - - for ( var i = 0, l = this.stride; i < l; i ++ ) { - - this.array[ index1 + i ] = attribute.array[ index2 + i ]; - - } - - return this; - - }, - - set: function ( value, offset ) { - - if ( offset === undefined ) offset = 0; - - this.array.set( value, offset ); - - return this; - - }, - - clone: function () { - - return new this.constructor().copy( this ); - - }, - - onUpload: function ( callback ) { - - this.onUploadCallback = callback; - - return this; - - } - - } ); - /** * @author benaadams / https://twitter.com/ben_a_adams */ @@ -41973,9 +44256,19 @@ * @author benaadams / https://twitter.com/ben_a_adams */ - function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { + function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { + + if ( typeof ( normalized ) === 'number' ) { + + meshPerAttribute = normalized; + + normalized = false; + + console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); + + } - BufferAttribute.call( this, array, itemSize ); + BufferAttribute.call( this, array, itemSize, normalized ); this.meshPerAttribute = meshPerAttribute || 1; @@ -42129,92 +44422,21 @@ } ); - /** - * @author alteredq / http://alteredqualia.com/ - */ - - function Clock( autoStart ) { - - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; - - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; - - this.running = false; - - } - - Object.assign( Clock.prototype, { - - start: function () { - - this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; - - }, - - stop: function () { - - this.getElapsedTime(); - this.running = false; - this.autoStart = false; - - }, - - getElapsedTime: function () { - - this.getDelta(); - return this.elapsedTime; - - }, - - getDelta: function () { - - var diff = 0; - - if ( this.autoStart && ! this.running ) { - - this.start(); - return 0; - - } - - if ( this.running ) { - - var newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); - - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; - - this.elapsedTime += diff; - - } - - return diff; - - } - - } ); - /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley * * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * - * The poles (phi) are at the positive and negative y axis. - * The equator starts at positive z. + * 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-axiz. */ function Spherical( radius, phi, theta ) { this.radius = ( radius !== undefined ) ? radius : 1.0; - this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole - this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere + this.phi = ( phi !== undefined ) ? phi : 0; // polar angle + this.theta = ( theta !== undefined ) ? theta : 0; // azimuthal angle return this; @@ -42258,9 +44480,15 @@ }, - setFromVector3: function ( vec3 ) { + setFromVector3: function ( v ) { - this.radius = vec3.length(); + return this.setFromCartesianCoords( v.x, v.y, v.z ); + + }, + + setFromCartesianCoords: function ( x, y, z ) { + + this.radius = Math.sqrt( x * x + y * y + z * z ); if ( this.radius === 0 ) { @@ -42269,8 +44497,8 @@ } else { - this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis - this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle + this.theta = Math.atan2( x, z ); + this.phi = Math.acos( _Math.clamp( y / this.radius, - 1, 1 ) ); } @@ -42325,11 +44553,17 @@ }, - setFromVector3: function ( vec3 ) { + setFromVector3: function ( v ) { - this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); - this.theta = Math.atan2( vec3.x, vec3.z ); - this.y = vec3.y; + return this.setFromCartesianCoords( v.x, v.y, v.z ); + + }, + + setFromCartesianCoords: function ( x, y, z ) { + + this.radius = Math.sqrt( x * x + z * z ); + this.theta = Math.atan2( x, z ); + this.y = y; return this; @@ -42577,6 +44811,153 @@ } ); + /** + * @author bhouston / http://clara.io + */ + + function Line3( start, end ) { + + this.start = ( start !== undefined ) ? start : new Vector3(); + this.end = ( end !== undefined ) ? end : new Vector3(); + + } + + Object.assign( Line3.prototype, { + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + getCenter: function ( target ) { + + if ( target === undefined ) { + + console.warn( 'THREE.Line3: .getCenter() target is now required' ); + target = new Vector3(); + + } + + return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( target ) { + + if ( target === undefined ) { + + console.warn( 'THREE.Line3: .delta() target is now required' ); + target = new Vector3(); + + } + + return target.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, target ) { + + if ( target === undefined ) { + + console.warn( 'THREE.Line3: .at() target is now required' ); + target = new Vector3(); + + } + + return this.delta( target ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function () { + + var startP = new Vector3(); + var startEnd = new Vector3(); + + return function closestPointToPointParameter( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = _Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, target ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + if ( target === undefined ) { + + console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); + target = new Vector3(); + + } + + return this.delta( target ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + } + + } ); + /** * @author alteredq / http://alteredqualia.com/ */ @@ -42803,7 +45184,6 @@ SpotLightHelper.prototype.update = function () { var vector = new Vector3(); - var vector2 = new Vector3(); return function update() { @@ -42814,10 +45194,9 @@ this.cone.scale.set( coneWidth, coneWidth, coneLength ); - vector.setFromMatrixPosition( this.light.matrixWorld ); - vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + vector.setFromMatrixPosition( this.light.target.matrixWorld ); - this.cone.lookAt( vector2.sub( vector ) ); + this.cone.lookAt( vector ); if ( this.color !== undefined ) { @@ -42974,7 +45353,7 @@ /* - var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); @@ -43040,73 +45419,75 @@ * @author abelnation / http://github.com/abelnation * @author Mugen87 / http://github.com/Mugen87 * @author WestLangley / http://github.com/WestLangley + * + * This helper must be added as a child of the light */ function RectAreaLightHelper( light, color ) { - Object3D.call( this ); + this.type = 'RectAreaLightHelper'; this.light = light; - this.light.updateMatrixWorld(); - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; + this.color = color; // optional hardwired color for the helper - this.color = color; + var positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ]; + + var geometry = new BufferGeometry(); + geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + geometry.computeBoundingSphere(); var material = new LineBasicMaterial( { fog: false } ); - var geometry = new BufferGeometry(); + Line.call( this, geometry, material ); - geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) ); + // - this.line = new Line( geometry, material ); - this.add( this.line ); + var positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ]; + var geometry2 = new BufferGeometry(); + geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); + geometry2.computeBoundingSphere(); + + this.add( new Mesh( geometry2, new MeshBasicMaterial( { side: BackSide, fog: false } ) ) ); this.update(); } - RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); + RectAreaLightHelper.prototype = Object.create( Line.prototype ); RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; - RectAreaLightHelper.prototype.dispose = function () { - - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); - - }; - RectAreaLightHelper.prototype.update = function () { - // calculate new dimensions of the helper + this.scale.set( 0.5 * this.light.width, 0.5 * this.light.height, 1 ); - var hx = this.light.width * 0.5; - var hy = this.light.height * 0.5; + if ( this.color !== undefined ) { - var position = this.line.geometry.attributes.position; - var array = position.array; + this.material.color.set( this.color ); + this.children[ 0 ].material.color.set( this.color ); - // update vertices + } else { - array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; - array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; - array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; - array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0; - array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0; + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - position.needsUpdate = true; + // prevent hue shift + var c = this.material.color; + var max = Math.max( c.r, c.g, c.b ); + if ( max > 1 ) c.multiplyScalar( 1 / max ); - if ( this.color !== undefined ) { + this.children[ 0 ].material.color.copy( this.material.color ); - this.line.material.color.set( this.color ); + } - } else { + }; - this.line.material.color.copy( this.light.color ); + RectAreaLightHelper.prototype.dispose = function () { - } + this.geometry.dispose(); + this.material.dispose(); + this.children[ 0 ].geometry.dispose(); + this.children[ 0 ].material.dispose(); }; @@ -43195,6 +45576,156 @@ }(); + /** + * @author WestLangley / http://github.com/WestLangley + */ + + function LightProbeHelper( lightProbe, size ) { + + this.lightProbe = lightProbe; + + this.size = size; + + var defines = {}; + defines[ 'GAMMA_OUTPUT' ] = ""; + + // material + var material = new ShaderMaterial( { + + defines: defines, + + uniforms: { + + sh: { value: this.lightProbe.sh.coefficients }, // by reference + + intensity: { value: this.lightProbe.intensity } + + }, + + vertexShader: [ + + 'varying vec3 vNormal;', + + 'void main() {', + + ' vNormal = normalize( normalMatrix * normal );', + + ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + + '}', + + ].join( '\n' ), + + fragmentShader: [ + + '#define RECIPROCAL_PI 0.318309886', + + 'vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {', + + ' // matrix is assumed to be orthogonal', + + ' return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );', + + '}', + + 'vec3 linearToOutput( in vec3 a ) {', + + ' #ifdef GAMMA_OUTPUT', + + ' return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );', + + ' #else', + + ' return a;', + + ' #endif', + + '}', + + '// source: https://graphics.stanford.edu/papers/envmap/envmap.pdf', + 'vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {', + + ' // normal is assumed to have unit length', + + ' float x = normal.x, y = normal.y, z = normal.z;', + + ' // band 0', + ' vec3 result = shCoefficients[ 0 ] * 0.886227;', + + ' // band 1', + ' result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;', + ' result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;', + ' result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;', + + ' // band 2', + ' result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;', + ' result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;', + ' result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );', + ' result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;', + ' result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );', + + ' return result;', + + '}', + + 'uniform vec3 sh[ 9 ]; // sh coefficients', + + 'uniform float intensity; // light probe intensity', + + 'varying vec3 vNormal;', + + 'void main() {', + + ' vec3 normal = normalize( vNormal );', + + ' vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );', + + ' vec3 irradiance = shGetIrradianceAt( worldNormal, sh );', + + ' vec3 outgoingLight = RECIPROCAL_PI * irradiance * intensity;', + + ' outgoingLight = linearToOutput( outgoingLight );', + + ' gl_FragColor = vec4( outgoingLight, 1.0 );', + + '}' + + ].join( '\n' ) + + } ); + + var geometry = new SphereBufferGeometry( 1, 32, 16 ); + + Mesh.call( this, geometry, material ); + + this.onBeforeRender(); + + } + + LightProbeHelper.prototype = Object.create( Mesh.prototype ); + LightProbeHelper.prototype.constructor = LightProbeHelper; + + LightProbeHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); + + }; + + LightProbeHelper.prototype.onBeforeRender = function () { + + return function update() { + + this.position.copy( this.lightProbe.position ); + + this.scale.set( 1, 1, 1 ).multiplyScalar( this.size ); + + this.material.uniforms.intensity.value = this.lightProbe.intensity; + + }; + + }(); + /** * @author mrdoob / http://mrdoob.com/ */ @@ -43236,8 +45767,28 @@ } - GridHelper.prototype = Object.create( LineSegments.prototype ); - GridHelper.prototype.constructor = GridHelper; + GridHelper.prototype = Object.assign( Object.create( LineSegments.prototype ), { + + constructor: GridHelper, + + copy: function ( source ) { + + LineSegments.prototype.copy.call( this, source ); + + this.geometry.copy( source.geometry ); + this.material.copy( source.material ); + + return this; + + }, + + clone: function () { + + return new this.constructor().copy( this ); + + } + + } ); /** * @author mrdoob / http://mrdoob.com/ @@ -43326,6 +45877,106 @@ PolarGridHelper.prototype = Object.create( LineSegments.prototype ); PolarGridHelper.prototype.constructor = PolarGridHelper; + /** + * @author Mugen87 / http://github.com/Mugen87 + */ + + function PositionalAudioHelper( audio, range, divisionsInnerAngle, divisionsOuterAngle ) { + + this.audio = audio; + this.range = range || 1; + this.divisionsInnerAngle = divisionsInnerAngle || 16; + this.divisionsOuterAngle = divisionsOuterAngle || 2; + + var geometry = new BufferGeometry(); + var divisions = this.divisionsInnerAngle + this.divisionsOuterAngle * 2; + var positions = new Float32Array( ( divisions * 3 + 3 ) * 3 ); + geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); + + var materialInnerAngle = new LineBasicMaterial( { color: 0x00ff00 } ); + var materialOuterAngle = new LineBasicMaterial( { color: 0xffff00 } ); + + Line.call( this, geometry, [ materialOuterAngle, materialInnerAngle ] ); + + this.update(); + + } + + PositionalAudioHelper.prototype = Object.create( Line.prototype ); + PositionalAudioHelper.prototype.constructor = PositionalAudioHelper; + + PositionalAudioHelper.prototype.update = function () { + + var audio = this.audio; + var range = this.range; + var divisionsInnerAngle = this.divisionsInnerAngle; + var divisionsOuterAngle = this.divisionsOuterAngle; + + var coneInnerAngle = _Math.degToRad( audio.panner.coneInnerAngle ); + var coneOuterAngle = _Math.degToRad( audio.panner.coneOuterAngle ); + + var halfConeInnerAngle = coneInnerAngle / 2; + var halfConeOuterAngle = coneOuterAngle / 2; + + var start = 0; + var count = 0; + var i, stride; + + var geometry = this.geometry; + var positionAttribute = geometry.attributes.position; + + geometry.clearGroups(); + + // + + function generateSegment( from, to, divisions, materialIndex ) { + + var step = ( to - from ) / divisions; + + positionAttribute.setXYZ( start, 0, 0, 0 ); + count ++; + + for ( i = from; i < to; i += step ) { + + stride = start + count; + + positionAttribute.setXYZ( stride, Math.sin( i ) * range, 0, Math.cos( i ) * range ); + positionAttribute.setXYZ( stride + 1, Math.sin( Math.min( i + step, to ) ) * range, 0, Math.cos( Math.min( i + step, to ) ) * range ); + positionAttribute.setXYZ( stride + 2, 0, 0, 0 ); + + count += 3; + + } + + geometry.addGroup( start, count, materialIndex ); + + start += count; + count = 0; + + } + + // + + generateSegment( - halfConeOuterAngle, - halfConeInnerAngle, divisionsOuterAngle, 0 ); + generateSegment( - halfConeInnerAngle, halfConeInnerAngle, divisionsInnerAngle, 1 ); + generateSegment( halfConeInnerAngle, halfConeOuterAngle, divisionsOuterAngle, 0 ); + + // + + positionAttribute.needsUpdate = true; + + if ( coneInnerAngle === coneOuterAngle ) this.material[ 0 ].visible = false; + + }; + + PositionalAudioHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material[ 0 ].dispose(); + this.material[ 1 ].dispose(); + + }; + /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley @@ -43503,7 +46154,7 @@ v2.setFromMatrixPosition( this.light.target.matrixWorld ); v3.subVectors( v2, v1 ); - this.lightPlane.lookAt( v3 ); + this.lightPlane.lookAt( v2 ); if ( this.color !== undefined ) { @@ -43517,7 +46168,7 @@ } - this.targetLine.lookAt( v3 ); + this.targetLine.lookAt( v2 ); this.targetLine.scale.z = v3.length(); }; @@ -43675,10 +46326,10 @@ var w = 1, h = 1; - // we need just camera projection matrix + // we need just camera projection matrix inverse // world matrix must be identity - camera.projectionMatrix.copy( this.camera.projectionMatrix ); + camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); // center / target @@ -43820,6 +46471,22 @@ }; + BoxHelper.prototype.copy = function ( source ) { + + LineSegments.prototype.copy.call( this, source ); + + this.object = source.object; + + return this; + + }; + + BoxHelper.prototype.clone = function () { + + return new this.constructor().copy( this ); + + }; + /** * @author WestLangley / http://github.com/WestLangley */ @@ -43912,6 +46579,8 @@ this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); + this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here + this.lookAt( this.plane.normal ); Object3D.prototype.updateMatrixWorld.call( this, force ); @@ -43934,8 +46603,7 @@ * headWidth - Number */ - var lineGeometry; - var coneGeometry; + var lineGeometry, coneGeometry; function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { @@ -43943,8 +46611,10 @@ Object3D.call( this ); - if ( color === undefined ) color = 0xffff00; + if ( dir === undefined ) dir = new Vector3( 0, 0, 1 ); + if ( origin === undefined ) origin = new Vector3( 0, 0, 0 ); if ( length === undefined ) length = 1; + if ( color === undefined ) color = 0xffff00; if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; @@ -44028,6 +46698,23 @@ }; + ArrowHelper.prototype.copy = function ( source ) { + + Object3D.prototype.copy.call( this, source, false ); + + this.line.copy( source.line ); + this.cone.copy( source.cone ); + + return this; + + }; + + ArrowHelper.prototype.clone = function () { + + return new this.constructor().copy( this ); + + }; + /** * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ @@ -44422,6 +47109,17 @@ } + Object.assign( ObjectLoader.prototype, { + + setTexturePath: function ( value ) { + + console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' ); + return this.setResourcePath( value ); + + } + + } ); + // Object.assign( Box2.prototype, { @@ -44975,6 +47673,12 @@ } ); + SkinnedMesh.prototype.initBones = function () { + + console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); + + }; + Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { get: function () { @@ -45251,6 +47955,20 @@ } }, + + overdraw: { + get: function () { + + console.warn( 'THREE.Material: .overdraw has been removed.' ); + + }, + set: function () { + + console.warn( 'THREE.Material: .overdraw has been removed.' ); + + } + }, + wrapRGB: { get: function () { @@ -45317,34 +48035,43 @@ Object.assign( 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 ); + + }, + animate: function ( callback ) { + + console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); + this.setAnimationLoop( callback ); + + }, getCurrentRenderTarget: function () { console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); return this.getRenderTarget(); }, - getMaxAnisotropy: function () { console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); return this.capabilities.getMaxAnisotropy(); }, - getPrecision: function () { console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); return this.capabilities.precision; }, - resetGLState: function () { console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); return this.state.reset(); }, - supportsFloatTextures: function () { console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); @@ -45423,6 +48150,26 @@ console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + }, + allocTextureUnit: function () { + + console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + + }, + setTexture: function () { + + console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + + }, + setTexture2D: function () { + + console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + + }, + setTextureCube: function () { + + console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + } } ); @@ -45516,6 +48263,27 @@ // + Object.defineProperties( WebGLRenderTargetCube.prototype, { + + activeCubeFace: { + set: function ( /* value */ ) { + + console.warn( 'THREE.WebGLRenderTargetCube: .activeCubeFace has been removed. It is now the second parameter of WebGLRenderer.setRenderTarget().' ); + + } + }, + activeMipMapLevel: { + set: function ( /* value */ ) { + + console.warn( 'THREE.WebGLRenderTargetCube: .activeMipMapLevel has been removed. It is now the third parameter of WebGLRenderer.setRenderTarget().' ); + + } + } + + } ); + + // + Object.defineProperties( WebGLRenderTarget.prototype, { wrapS: { @@ -45670,6 +48438,13 @@ console.warn( 'THREE.WebVRManager: .standing has been removed.' ); + } + }, + userHeight: { + set: function ( /* value */ ) { + + console.warn( 'THREE.WebVRManager: .userHeight has been removed.' ); + } } @@ -45738,51 +48513,47 @@ }; - var ImageUtils = { - - crossOrigin: undefined, + ImageUtils.crossOrigin = undefined; - loadTexture: function ( url, mapping, onLoad, onError ) { + ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - - var loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); - var texture = loader.load( url, onLoad, undefined, onError ); + var loader = new TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - if ( mapping ) texture.mapping = mapping; + var texture = loader.load( url, onLoad, undefined, onError ); - return texture; + if ( mapping ) texture.mapping = mapping; - }, + return texture; - loadTextureCube: function ( urls, mapping, onLoad, onError ) { + }; - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); + ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { - var loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); - var texture = loader.load( urls, onLoad, undefined, onError ); + var loader = new CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); - if ( mapping ) texture.mapping = mapping; + var texture = loader.load( urls, onLoad, undefined, onError ); - return texture; + if ( mapping ) texture.mapping = mapping; - }, + return texture; - loadCompressedTexture: function () { + }; - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); + ImageUtils.loadCompressedTexture = function () { - }, + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); - loadCompressedTextureCube: function () { + }; - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + ImageUtils.loadCompressedTextureCube = function () { - } + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); }; @@ -45818,13 +48589,15 @@ function CanvasRenderer() { - console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); + console.error( 'THREE.CanvasRenderer has been removed' ); + + } - this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); - this.clear = function () {}; - this.render = function () {}; - this.setClearColor = function () {}; - this.setSize = function () {}; + // + + function JSONLoader() { + + console.error( 'THREE.JSONLoader has been removed.' ); } @@ -45860,6 +48633,7 @@ } + exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; @@ -45883,11 +48657,14 @@ exports.Group = Group; exports.VideoTexture = VideoTexture; exports.DataTexture = DataTexture; + exports.DataTexture2DArray = DataTexture2DArray; + exports.DataTexture3D = DataTexture3D; exports.CompressedTexture = CompressedTexture; exports.CubeTexture = CubeTexture; exports.CanvasTexture = CanvasTexture; exports.DepthTexture = DepthTexture; exports.Texture = Texture; + exports.AnimationLoader = AnimationLoader; exports.CompressedTextureLoader = CompressedTextureLoader; exports.DataTextureLoader = DataTextureLoader; exports.CubeTextureLoader = CubeTextureLoader; @@ -45897,7 +48674,6 @@ exports.BufferGeometryLoader = BufferGeometryLoader; exports.DefaultLoadingManager = DefaultLoadingManager; exports.LoadingManager = LoadingManager; - exports.JSONLoader = JSONLoader; exports.ImageLoader = ImageLoader; exports.ImageBitmapLoader = ImageBitmapLoader; exports.FontLoader = FontLoader; @@ -45911,11 +48687,14 @@ exports.PointLight = PointLight; exports.RectAreaLight = RectAreaLight; exports.HemisphereLight = HemisphereLight; + exports.HemisphereLightProbe = HemisphereLightProbe; exports.DirectionalLightShadow = DirectionalLightShadow; exports.DirectionalLight = DirectionalLight; exports.AmbientLight = AmbientLight; + exports.AmbientLightProbe = AmbientLightProbe; exports.LightShadow = LightShadow; exports.Light = Light; + exports.LightProbe = LightProbe; exports.StereoCamera = StereoCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.OrthographicCamera = OrthographicCamera; @@ -45978,6 +48757,7 @@ exports.Vector2 = Vector2; exports.Quaternion = Quaternion; exports.Color = Color; + exports.SphericalHarmonics3 = SphericalHarmonics3; exports.ImmediateRenderObject = ImmediateRenderObject; exports.VertexNormalsHelper = VertexNormalsHelper; exports.SpotLightHelper = SpotLightHelper; @@ -45985,8 +48765,10 @@ exports.PointLightHelper = PointLightHelper; exports.RectAreaLightHelper = RectAreaLightHelper; exports.HemisphereLightHelper = HemisphereLightHelper; + exports.LightProbeHelper = LightProbeHelper; exports.GridHelper = GridHelper; exports.PolarGridHelper = PolarGridHelper; + exports.PositionalAudioHelper = PositionalAudioHelper; exports.FaceNormalsHelper = FaceNormalsHelper; exports.DirectionalLightHelper = DirectionalLightHelper; exports.CameraHelper = CameraHelper; @@ -46001,6 +48783,7 @@ exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; + exports.ImageUtils = ImageUtils; exports.ShapeUtils = ShapeUtils; exports.WebGLUtils = WebGLUtils; exports.WireframeGeometry = WireframeGeometry; @@ -46044,6 +48827,7 @@ exports.CircleGeometry = CircleGeometry; exports.CircleBufferGeometry = CircleBufferGeometry; exports.BoxGeometry = BoxGeometry; + exports.CubeGeometry = BoxGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.ShadowMaterial = ShadowMaterial; exports.SpriteMaterial = SpriteMaterial; @@ -46059,6 +48843,7 @@ exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshDistanceMaterial = MeshDistanceMaterial; exports.MeshBasicMaterial = MeshBasicMaterial; + exports.MeshMatcapMaterial = MeshMatcapMaterial; exports.LineDashedMaterial = LineDashedMaterial; exports.LineBasicMaterial = LineBasicMaterial; exports.Material = Material; @@ -46139,6 +48924,7 @@ exports.ReinhardToneMapping = ReinhardToneMapping; exports.Uncharted2ToneMapping = Uncharted2ToneMapping; exports.CineonToneMapping = CineonToneMapping; + exports.ACESFilmicToneMapping = ACESFilmicToneMapping; exports.UVMapping = UVMapping; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; @@ -46176,6 +48962,7 @@ exports.RGBEFormat = RGBEFormat; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; + exports.RedFormat = RedFormat; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; @@ -46221,7 +49008,8 @@ exports.RGBDEncoding = RGBDEncoding; exports.BasicDepthPacking = BasicDepthPacking; exports.RGBADepthPacking = RGBADepthPacking; - exports.CubeGeometry = BoxGeometry; + exports.TangentSpaceNormalMap = TangentSpaceNormalMap; + exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; exports.Face4 = Face4; exports.LineStrip = LineStrip; exports.LinePieces = LinePieces; @@ -46254,12 +49042,12 @@ exports.XHRLoader = XHRLoader; exports.BinaryTextureLoader = BinaryTextureLoader; exports.GeometryUtils = GeometryUtils; - exports.ImageUtils = ImageUtils; exports.Projector = Projector; exports.CanvasRenderer = CanvasRenderer; + exports.JSONLoader = JSONLoader; exports.SceneUtils = SceneUtils; exports.LensFlare = LensFlare; Object.defineProperty(exports, '__esModule', { value: true }); -}))); +}));