From 1868fbd0cfacc674d2d7b00d0a2782cf09546b34 Mon Sep 17 00:00:00 2001 From: Sigve Sebastian Farstad Date: Sat, 20 Apr 2019 23:57:19 +0200 Subject: [PATCH 1/3] Fix terribly annying vim thing Without this, nin crashes all the time when using vim to edit files. --- nin/backend/watch.js | 4 ++++ 1 file changed, 4 insertions(+) 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) + From a21f110e17ed81cc53645adc0e08ec9fb5efb6bf Mon Sep 17 00:00:00 2001 From: Sigve Sebastian Farstad Date: Sat, 20 Apr 2019 23:58:06 +0200 Subject: [PATCH 2/3] Reset more when using RT pool Rendertargets from the rendertarget pool don't reset everything inbetween use. Maybe we want to reset everything really, but right now we need these specific things to be reset for the `si` demo, so this is a good place to start. --- nin/dasBoot/FullscreenRenderTargetPool.js | 2 ++ nin/dasBoot/NodeManager.js | 2 +- nin/dasBoot/RootNode.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nin/dasBoot/FullscreenRenderTargetPool.js b/nin/dasBoot/FullscreenRenderTargetPool.js index 238b4bb..386cd6f 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.repeat.set(1, 1); + renderTarget.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..aec3b06 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); From ce9ba52318f52e8dc99f82e354ffacc290bc7165 Mon Sep 17 00:00:00 2001 From: Sigve Sebastian Farstad Date: Wed, 10 Jul 2019 22:34:38 +0200 Subject: [PATCH 3/3] Fix some additional things needed for dvoje --- nin/dasBoot/FullscreenRenderTargetPool.js | 4 +- nin/dasBoot/RootNode.js | 2 + nin/dasBoot/ShaderNode.js | 5 +- nin/dasBoot/THREENode.js | 5 +- nin/dasBoot/lib/00_three.js | 23614 +++++++++++--------- 5 files changed, 13213 insertions(+), 10417 deletions(-) mode change 100755 => 100644 nin/dasBoot/lib/00_three.js diff --git a/nin/dasBoot/FullscreenRenderTargetPool.js b/nin/dasBoot/FullscreenRenderTargetPool.js index 386cd6f..66d123f 100644 --- a/nin/dasBoot/FullscreenRenderTargetPool.js +++ b/nin/dasBoot/FullscreenRenderTargetPool.js @@ -21,8 +21,8 @@ class FullscreenRenderTargetPool { this.renderTargets[this.used] = renderTarget; } this.used++; - renderTarget.repeat.set(1, 1); - renderTarget.offset.set(0, 0); + renderTarget.texture.repeat.set(1, 1); + renderTarget.texture.offset.set(0, 0); return renderTarget; } diff --git a/nin/dasBoot/RootNode.js b/nin/dasBoot/RootNode.js index aec3b06..3ba0bb1 100644 --- a/nin/dasBoot/RootNode.js +++ b/nin/dasBoot/RootNode.js @@ -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 }); -}))); +}));