Browse Source

Merge pull request #11 from mrdoob/dev

Update
Temdog007 6 years ago
parent
commit
976f1b6f8e
100 changed files with 2637 additions and 2315 deletions
  1. 1 1
      .github/ISSUE_TEMPLATE.md
  2. 337 163
      build/three.js
  3. 295 409
      build/three.min.js
  4. 30 43
      build/three.module.js
  5. 2 2
      docs/api/en/cameras/ArrayCamera.html
  6. 1 1
      docs/api/en/extras/core/Path.html
  7. 1 1
      docs/api/en/extras/core/Shape.html
  8. 1 1
      docs/api/en/extras/core/ShapePath.html
  9. 1 1
      docs/api/en/extras/curves/ArcCurve.html
  10. 1 1
      docs/api/en/geometries/ConeBufferGeometry.html
  11. 1 1
      docs/api/en/geometries/ConeGeometry.html
  12. 1 1
      docs/api/en/geometries/DodecahedronBufferGeometry.html
  13. 2 0
      docs/api/en/geometries/ExtrudeBufferGeometry.html
  14. 2 0
      docs/api/en/geometries/ExtrudeGeometry.html
  15. 1 1
      docs/api/en/geometries/IcosahedronBufferGeometry.html
  16. 1 1
      docs/api/en/geometries/OctahedronBufferGeometry.html
  17. 1 1
      docs/api/en/geometries/TetrahedronBufferGeometry.html
  18. 3 1
      docs/api/en/geometries/TextBufferGeometry.html
  19. 3 1
      docs/api/en/geometries/TextGeometry.html
  20. 1 1
      docs/api/en/helpers/AxesHelper.html
  21. 1 1
      docs/api/en/helpers/Box3Helper.html
  22. 1 1
      docs/api/en/helpers/BoxHelper.html
  23. 1 1
      docs/api/en/helpers/CameraHelper.html
  24. 1 1
      docs/api/en/helpers/FaceNormalsHelper.html
  25. 2 2
      docs/api/en/helpers/GridHelper.html
  26. 1 1
      docs/api/en/helpers/PlaneHelper.html
  27. 1 1
      docs/api/en/helpers/PointLightHelper.html
  28. 1 1
      docs/api/en/helpers/PolarGridHelper.html
  29. 1 1
      docs/api/en/helpers/VertexNormalsHelper.html
  30. 2 2
      docs/api/en/materials/MeshToonMaterial.html
  31. 1 1
      docs/api/en/materials/RawShaderMaterial.html
  32. 2 2
      docs/api/en/objects/LOD.html
  33. 9 0
      docs/api/en/renderers/WebGLRenderer.html
  34. 1 1
      docs/api/zh/cameras/ArrayCamera.html
  35. 2 2
      docs/api/zh/materials/MeshToonMaterial.html
  36. 2 2
      docs/api/zh/objects/LOD.html
  37. 9 0
      docs/api/zh/renderers/WebGLRenderer.html
  38. 1 1
      docs/examples/controls/OrbitControls.html
  39. 1 1
      docs/examples/loaders/MTLLoader.html
  40. 3 3
      docs/examples/loaders/OBJLoader.html
  41. 1 1
      docs/examples/loaders/OBJLoader2.html
  42. 78 62
      docs/examples/quickhull/QuickHull.html
  43. BIN
      docs/files/inconsolata.woff
  44. 0 213
      docs/index.css
  45. 28 32
      docs/index.html
  46. 65 4
      docs/manual/en/introduction/Import-via-modules.html
  47. 6 0
      docs/manual/en/introduction/Useful-links.html
  48. 58 25
      docs/page.css
  49. 1 2
      docs/page.js
  50. 4 2
      docs/prettify/threejs.css
  51. 5 16
      docs/scenes/bones-browser.html
  52. 5 16
      docs/scenes/geometry-browser.html
  53. 14 4
      docs/scenes/js/geometry.js
  54. 53 0
      docs/scenes/js/material.js
  55. 5 16
      docs/scenes/material-browser.html
  56. 1 0
      editor/index.html
  57. 123 123
      editor/js/Menubar.Add.js
  58. 62 0
      editor/js/Menubar.Edit.js
  59. 1 4
      editor/js/Menubar.File.js
  60. 25 3
      editor/js/Sidebar.Material.js
  61. 3 3
      editor/js/Sidebar.Settings.js
  62. 28 5
      editor/js/Strings.js
  63. 20 3
      editor/js/libs/ui.three.js
  64. 1 1
      editor/sw.js
  65. 2 2
      examples/css2d_label.html
  66. 2 2
      examples/css3d_orthographic.html
  67. 2 2
      examples/css3d_sandbox.html
  68. 1 1
      examples/css3d_youtube.html
  69. 5 3
      examples/files.js
  70. BIN
      examples/files/inconsolata.woff
  71. 26 221
      examples/index.html
  72. 102 0
      examples/js/QuickHull.js
  73. 1 1
      examples/js/controls/DragControls.js
  74. 23 23
      examples/js/controls/PointerLockControls.js
  75. 1 1
      examples/js/controls/TrackballControls.js
  76. 1 0
      examples/js/controls/TransformControls.js
  77. 6 6
      examples/js/effects/AnaglyphEffect.js
  78. 46 4
      examples/js/effects/OutlineEffect.js
  79. 32 10
      examples/js/exporters/GLTFExporter.js
  80. 163 159
      examples/js/geometries/LightningStrike.js
  81. 3 3
      examples/js/interactive/SelectionHelper.js
  82. 121 0
      examples/js/lights/LightProbeGenerator.js
  83. 48 76
      examples/js/loaders/LDrawLoader.js
  84. 11 8
      examples/js/loaders/LoaderSupport.js
  85. 117 74
      examples/js/loaders/OBJLoader2.js
  86. 1 1
      examples/js/loaders/PCDLoader.js
  87. 7 4
      examples/js/loaders/SVGLoader.js
  88. 3 3
      examples/js/math/ColorConverter.js
  89. 59 355
      examples/js/math/Lut.js
  90. 2 12
      examples/js/objects/Reflector.js
  91. 2 12
      examples/js/objects/Refractor.js
  92. 6 6
      examples/js/postprocessing/SSAOPass.js
  93. 2 1
      examples/js/renderers/CSS2DRenderer.js
  94. 3 2
      examples/js/utils/ShadowMapViewer.js
  95. 56 45
      examples/jsm/controls/OrbitControls.d.ts
  96. 9 5
      examples/jsm/exporters/ColladaExporter.js
  97. 32 10
      examples/jsm/exporters/GLTFExporter.js
  98. 37 32
      examples/jsm/loaders/GLTFLoader.js
  99. 357 0
      examples/jsm/loaders/STLLoader.js
  100. 32 40
      examples/jsm/utils/MathUtils.js

+ 1 - 1
.github/ISSUE_TEMPLATE.md

@@ -19,7 +19,7 @@ Please also include a live example if possible. You can start from these templat
 ##### Three.js version
 
 - [ ] Dev
-- [ ] r103
+- [ ] r104
 - [ ] ...
 
 ##### Browser

+ 337 - 163
build/three.js

@@ -185,7 +185,7 @@
 
 	} );
 
-	var REVISION = '104dev';
+	var REVISION = '105dev';
 	var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
 	var CullFaceNone = 0;
 	var CullFaceBack = 1;
@@ -761,21 +761,14 @@
 
 		},
 
-		clampScalar: function () {
-
-			var min = new Vector2();
-			var max = new Vector2();
-
-			return function clampScalar( minVal, maxVal ) {
+		clampScalar: function ( minVal, maxVal ) {
 
-				min.set( minVal, minVal );
-				max.set( maxVal, maxVal );
+			this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
+			this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
 
-				return this.clamp( min, max );
-
-			};
+			return this;
 
-		}(),
+		},
 
 		clampLength: function ( min, max ) {
 
@@ -1993,21 +1986,15 @@
 
 		},
 
-		clampScalar: function () {
-
-			var min = new Vector3();
-			var max = new Vector3();
-
-			return function clampScalar( minVal, maxVal ) {
-
-				min.set( minVal, minVal, minVal );
-				max.set( maxVal, maxVal, maxVal );
+		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 ) );
+			this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
 
-			};
+			return this;
 
-		}(),
+		},
 
 		clampLength: function ( min, max ) {
 
@@ -8280,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 );
 
@@ -11888,7 +11877,7 @@
 
 							for ( var j = 0, jl = morphAttribute.count; j < jl; j ++ ) {
 
-								vector.fromBufferAttribute( morphAttribute, i );
+								vector.fromBufferAttribute( morphAttribute, j );
 
 								maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );
 
@@ -12055,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 ];
 
@@ -16867,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, 35713 ) === 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 === 35633 ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
+				console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === 35633 ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );
+
+			}
 
 		}
 
@@ -17473,8 +17467,8 @@
 		// console.log( '*VERTEX*', vertexGlsl );
 		// console.log( '*FRAGMENT*', fragmentGlsl );
 
-		var glVertexShader = WebGLShader( gl, 35633, vertexGlsl );
-		var glFragmentShader = WebGLShader( gl, 35632, 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 );
@@ -17494,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, 35714 ) === 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(), '35715', gl.getProgramParameter( program, 35715 ), '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
 
-			};
+					}
+
+				};
+
+			}
 
 		}
 
@@ -22697,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;
@@ -24552,7 +24561,7 @@
 
 				} else if ( material.isShadowMaterial ) {
 
-					m_uniforms.color.value = material.color;
+					m_uniforms.color.value.copy( material.color );
 					m_uniforms.opacity.value = material.opacity;
 
 				}
@@ -24598,7 +24607,7 @@
 
 			if ( material.color ) {
 
-				uniforms.diffuse.value = material.color;
+				uniforms.diffuse.value.copy( material.color );
 
 			}
 
@@ -24728,7 +24737,7 @@
 
 		function refreshUniformsLine( uniforms, material ) {
 
-			uniforms.diffuse.value = material.color;
+			uniforms.diffuse.value.copy( material.color );
 			uniforms.opacity.value = material.opacity;
 
 		}
@@ -24743,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;
@@ -24766,7 +24775,7 @@
 
 		function refreshUniformsSprites( uniforms, material ) {
 
-			uniforms.diffuse.value = material.color;
+			uniforms.diffuse.value.copy( material.color );
 			uniforms.opacity.value = material.opacity;
 			uniforms.rotation.value = material.rotation;
 			uniforms.map.value = material.map;
@@ -24787,7 +24796,7 @@
 
 		function refreshUniformsFog( uniforms, fog ) {
 
-			uniforms.fogColor.value = fog.color;
+			uniforms.fogColor.value.copy( fog.color );
 
 			if ( fog.isFog ) {
 
@@ -24814,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 ) {
@@ -25885,6 +25894,8 @@
 
 			this.add( object );
 
+			return this;
+
 		},
 
 		getObjectForDistance: function ( distance ) {
@@ -29381,7 +29392,8 @@
 	 *
 	 *  bevelEnabled: <bool>, // turn on bevel
 	 *  bevelThickness: <float>, // how deep into the original shape bevel goes
-	 *  bevelSize: <float>, // how far from shape outline is bevel
+	 *  bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel
+	 *  bevelOffset: <float>, // how far from shape outline does bevel start
 	 *  bevelSegments: <int>, // number of bevel layers
 	 *
 	 *  extrudePath: <THREE.Curve> // curve to extrude shape along
@@ -29472,6 +29484,7 @@
 			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;
@@ -29520,6 +29533,7 @@
 				bevelSegments = 0;
 				bevelThickness = 0;
 				bevelSize = 0;
+				bevelOffset = 0;
 
 			}
 
@@ -29756,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
 
@@ -29787,7 +29801,7 @@
 
 			}
 
-			bs = bevelSize;
+			bs = bevelSize + bevelOffset;
 
 			// Back facing vertices
 
@@ -29854,7 +29868,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
 
@@ -30206,7 +30220,8 @@
 	 *
 	 *  bevelEnabled: <bool>, // turn on bevel
 	 *  bevelThickness: <float>, // how deep into text bevel goes
-	 *  bevelSize: <float> // how far from text outline is bevel
+	 *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
+	 *  bevelOffset: <float> // how far from text outline does bevel start
 	 * }
 	 */
 
@@ -40299,6 +40314,35 @@
 
 			return new this.constructor().copy( this );
 
+		},
+
+		fromArray: function ( array ) {
+
+			var coefficients = this.coefficients;
+
+			for ( var i = 0; i < 9; i ++ ) {
+
+				coefficients[ i ].fromArray( array, i * 3 );
+
+			}
+
+			return this;
+
+		},
+
+		toArray: function () {
+
+			var array = [];
+			var coefficients = this.coefficients;
+
+			for ( var i = 0; i < 9; i ++ ) {
+
+				coefficients[ i ].toArray( array, i * 3 );
+
+			}
+
+			return array;
+
 		}
 
 	} );
@@ -40334,19 +40378,15 @@
 
 	/**
 	 * @author WestLangley / http://github.com/WestLangley
+	 *
+	 * A LightProbe is a source of indirect-diffuse light
 	 */
 
-	// A LightProbe is a source of indirect-diffuse light
+	function LightProbe( sh, intensity ) {
 
-	function LightProbe( color, intensity ) {
+		Light.call( this, undefined, intensity );
 
-		Light.call( this, color, intensity );
-
-		this.sh = new SphericalHarmonics3();
-
-		this.sh.coefficients[ 0 ].set( this.color.r, this.color.g, this.color.b );
-
-		this.type = 'LightProbe';
+		this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3();
 
 	}
 
@@ -40356,122 +40396,102 @@
 
 		isLightProbe: true,
 
-		// https://www.ppsloan.org/publications/StupidSH36.pdf
-		setFromCubeTexture: function ( cubeTexture ) {
-
-			var norm, lengthSq, weight, totalWeight = 0;
-
-			var coord = new Vector3();
-
-			var dir = new Vector3();
-
-			var color = new Color();
-
-			var shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
-
-			var shCoefficients = this.sh.coefficients;
-
-			for ( var faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
-
-				var image = cubeTexture.image[ faceIndex ];
-
-				var width = image.width;
-				var height = image.height;
+		copy: function ( source ) {
 
-				var canvas = document.createElement( 'canvas' );
+			Light.prototype.copy.call( this, source );
 
-				canvas.width = width;
-				canvas.height = height;
+			this.sh.copy( source.sh );
+			this.intensity = source.intensity;
 
-				var context = canvas.getContext( '2d' );
+			return this;
 
-				context.drawImage( image, 0, 0, width, height );
+		},
 
-				var imageData = context.getImageData( 0, 0, width, height );
+		toJSON: function ( meta ) {
 
-				var data = imageData.data;
+			var data = Light.prototype.toJSON.call( this, meta );
 
-				var imageWidth = imageData.width; // assumed to be square
+			// data.sh = this.sh.toArray(); // todo
 
-				var pixelSize = 2 / imageWidth;
+			return data;
 
-				for ( var i = 0, il = data.length; i < il; i += 4 ) { // RGBA assumed
+		}
 
-					// pixel color
-					color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
+	} );
 
-					// convert to linear color space
-					color.copySRGBToLinear( color );
+	/**
+	 * @author WestLangley / http://github.com/WestLangley
+	 */
 
-					// pixel coordinate on unit cube
+	function HemisphereLightProbe( skyColor, groundColor, intensity ) {
 
-					var pixelIndex = i / 4;
+		LightProbe.call( this, undefined, intensity );
 
-					var col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+		var color1 = new Color().set( skyColor );
+		var color2 = new Color().set( groundColor );
 
-					var row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
+		var sky = new Vector3( color1.r, color1.g, color1.b );
+		var ground = new Vector3( color2.r, color2.g, color2.b );
 
-					switch ( faceIndex ) {
+		// 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 );
 
-						case 0: coord.set( - 1, row, - col ); break;
+		this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 );
+		this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 );
 
-						case 1: coord.set( 1, row, col ); break;
+	}
 
-						case 2: coord.set( - col, 1, - row ); break;
+	HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), {
 
-						case 3: coord.set( - col, - 1, row ); break;
+		constructor: HemisphereLightProbe,
 
-						case 4: coord.set( - col, row, 1 ); break;
+		isHemisphereLightProbe: true,
 
-						case 5: coord.set( col, row, - 1 ); break;
+		copy: function ( source ) { // modifying colors not currently supported
 
-					}
+			LightProbe.prototype.copy.call( this, source );
 
-					// weight assigned to this pixel
+			return this;
 
-					lengthSq = coord.lengthSq();
+		},
 
-					weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
+		toJSON: function ( meta ) {
 
-					totalWeight += weight;
+			var data = LightProbe.prototype.toJSON.call( this, meta );
 
-					// direction vector to this pixel
-					dir.copy( coord ).normalize();
+			// data.sh = this.sh.toArray(); // todo
 
-					// evaluate SH basis functions in direction dir
-					SphericalHarmonics3.getBasisAt( dir, shBasis );
+			return data;
 
-					// accummuulate
-					for ( var j = 0; j < 9; j ++ ) {
+		}
 
-						shCoefficients[ j ].x += shBasis[ j ] * color.r * weight;
-						shCoefficients[ j ].y += shBasis[ j ] * color.g * weight;
-						shCoefficients[ j ].z += shBasis[ j ] * color.b * weight;
+	} );
 
-					}
+	/**
+	 * @author WestLangley / http://github.com/WestLangley
+	 */
 
-				}
+	function AmbientLightProbe( color, intensity ) {
 
-			}
+		LightProbe.call( this, undefined, intensity );
 
-			// normalize
-			norm = ( 4 * Math.PI ) / totalWeight;
+		var color1 = new Color().set( color );
 
-			for ( var j = 0; j < 9; j ++ ) {
+		// 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 ) );
 
-				shCoefficients[ j ].x *= norm;
-				shCoefficients[ j ].y *= norm;
-				shCoefficients[ j ].z *= norm;
+	}
 
-			}
+	AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), {
 
-		},
+		constructor: AmbientLightProbe,
 
-		copy: function ( source ) {
+		isAmbientLightProbe: true,
 
-			Light.prototype.copy.call( this, source );
+		copy: function ( source ) { // modifying color not currently supported
 
-			this.sh.copy( source.sh );
+			LightProbe.prototype.copy.call( this, source );
 
 			return this;
 
@@ -40479,9 +40499,9 @@
 
 		toJSON: function ( meta ) {
 
-			var data = Light.prototype.toJSON.call( this, meta );
+			var data = LightProbe.prototype.toJSON.call( this, meta );
 
-			//data.sh = this.sh.toArray(); // todo
+			// data.sh = this.sh.toArray(); // todo
 
 			return data;
 
@@ -41249,6 +41269,7 @@
 		Audio.call( this, listener );
 
 		this.panner = this.context.createPanner();
+		this.panner.panningModel = 'HRTF';
 		this.panner.connect( this.gain );
 
 	}
@@ -45556,6 +45577,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/
 	 */
@@ -48517,9 +48688,11 @@
 	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;
@@ -48593,6 +48766,7 @@
 	exports.PointLightHelper = PointLightHelper;
 	exports.RectAreaLightHelper = RectAreaLightHelper;
 	exports.HemisphereLightHelper = HemisphereLightHelper;
+	exports.LightProbeHelper = LightProbeHelper;
 	exports.GridHelper = GridHelper;
 	exports.PolarGridHelper = PolarGridHelper;
 	exports.PositionalAudioHelper = PositionalAudioHelper;

File diff suppressed because it is too large
+ 295 - 409
build/three.min.js


File diff suppressed because it is too large
+ 30 - 43
build/three.module.js


+ 2 - 2
docs/api/en/cameras/ArrayCamera.html

@@ -8,13 +8,13 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:PerspectiveCamera] &rarr;
+		[page:Object3D] &rarr; [page:Camera] &rarr; [page:PerspectiveCamera] &rarr;
 
 		<h1>[name]</h1>
 
 		<p class="desc">
 			[name] can be used in order to efficiently render a scene with a predefined set of cameras. This is an important performance aspect for rendering VR scenes.<br />
-			An instance of [name] always has an array of sub cameras. It's mandatory to define for each sub camera the *bounds* property which determines the part of the viewport that is rendered with this camera.
+			An instance of [name] always has an array of sub cameras. It's mandatory to define for each sub camera the *viewport* property which determines the part of the viewport that is rendered with this camera.
 		</p>
 
 		<h2>Example</h2>

+ 1 - 1
docs/api/en/extras/core/Path.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:CurvePath] &rarr;
+		[page:Curve] &rarr; [page:CurvePath] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/extras/core/Shape.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:Path] &rarr;
+		[page:Curve] &rarr; [page:CurvePath] &rarr; [page:Path] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/extras/core/ShapePath.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:CurvePath] &rarr;
+		[page:Curve] &rarr; [page:CurvePath] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/extras/curves/ArcCurve.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:EllipseCurve] &rarr;
+		[page:Curve] &rarr; [page:EllipseCurve] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/geometries/ConeBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:CylinderBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:CylinderBufferGeometry] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/geometries/ConeGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:CylinderGeometry] &rarr;
+		[page:Geometry] &rarr; [page:CylinderGeometry] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/geometries/DodecahedronBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:PolyhedronBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:PolyhedronBufferGeometry] &rarr;
 
 		<h1>[name]</h1>
 

+ 2 - 0
docs/api/en/geometries/ExtrudeBufferGeometry.html

@@ -51,6 +51,7 @@
 			bevelEnabled: true,
 			bevelThickness: 1,
 			bevelSize: 1,
+			bevelOffset: 0,
 			bevelSegments: 1
 		};
 
@@ -76,6 +77,7 @@
 				<li>bevelEnabled — bool. Apply beveling to the shape. Default is true.</li>
 				<li>bevelThickness — float. How deep into the original shape the bevel goes. Default is 6.</li>
 				<li>bevelSize — float. Distance from the shape outline that the bevel extends. Default is bevelThickness - 2.</li>
+				<li>bevelOffset — float. Distance from the shape outline that the bevel starts. Default is 0.</li>
 				<li>bevelSegments — int. Number of bevel layers. Default is 3.</li>
 				<li>extrudePath — THREE.CurvePath. A 3D spline path along which the shape should be extruded.</li>
 				<li>UVGenerator —  Object. object that provides UV generator functions</li>

+ 2 - 0
docs/api/en/geometries/ExtrudeGeometry.html

@@ -51,6 +51,7 @@
 			bevelEnabled: true,
 			bevelThickness: 1,
 			bevelSize: 1,
+			bevelOffset: 0,
 			bevelSegments: 1
 		};
 
@@ -76,6 +77,7 @@
 				<li>bevelEnabled — bool. Apply beveling to the shape. Default is true.</li>
 				<li>bevelThickness — float. How deep into the original shape the bevel goes. Default is 6.</li>
 				<li>bevelSize — float. Distance from the shape outline that the bevel extends. Default is bevelThickness - 2.</li>
+				<li>bevelOffset — float. Distance from the shape outline that the bevel starts. Default is 0.</li>
 				<li>bevelSegments — int. Number of bevel layers. Default is 3.</li>
 				<li>extrudePath — THREE.CurvePath. A 3D spline path along which the shape should be extruded.</li>
 				<li>UVGenerator —  Object. object that provides UV generator functions</li>

+ 1 - 1
docs/api/en/geometries/IcosahedronBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:PolyhedronBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:PolyhedronBufferGeometry] &rarr;
 		<h1>[name]</h1>
 
 		<p class="desc">A class for generating an icosahedron geometry.</p>

+ 1 - 1
docs/api/en/geometries/OctahedronBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:PolyhedronBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:PolyhedronBufferGeometry] &rarr;
 		<h1>[name]</h1>
 
 		<p class="desc">A class for generating an octahedron geometry.</p>

+ 1 - 1
docs/api/en/geometries/TetrahedronBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:PolyhedronBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:PolyhedronBufferGeometry] &rarr;
 
 		<h1>[name]</h1>
 

+ 3 - 1
docs/api/en/geometries/TextBufferGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:ExtrudeBufferGeometry] &rarr;
+		[page:BufferGeometry] &rarr; [page:ExtrudeBufferGeometry] &rarr;
 
 		<h1>[name]</h1>
 
@@ -55,6 +55,7 @@
 				bevelEnabled: true,
 				bevelThickness: 10,
 				bevelSize: 8,
+				bevelOffset: 0,
 				bevelSegments: 5
 			} );
 		} );
@@ -74,6 +75,7 @@
 			<li>bevelEnabled — Boolean. Turn on bevel. Default is False.</li>
 			<li>bevelThickness — Float. How deep into text bevel goes. Default is 10.</li>
 			<li>bevelSize — Float. How far from text outline is bevel. Default is 8.</li>
+			<li>bevelOffset — Float. How far from text outline bevel starts. Default is 0.</li>
 			<li>bevelSegments — Integer. Number of bevel segments. Default is 3.</li>
 		</ul>
 		</p>

+ 3 - 1
docs/api/en/geometries/TextGeometry.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:ExtrudeGeometry] &rarr;
+		[page:Geometry] &rarr; [page:ExtrudeGeometry] &rarr;
 
 		<h1>[name]</h1>
 
@@ -55,6 +55,7 @@
 				bevelEnabled: true,
 				bevelThickness: 10,
 				bevelSize: 8,
+				bevelOffset: 0,
 				bevelSegments: 5
 			} );
 		} );
@@ -74,6 +75,7 @@
 			<li>bevelEnabled — Boolean. Turn on bevel. Default is False.</li>
 			<li>bevelThickness — Float. How deep into text bevel goes. Default is 10.</li>
 			<li>bevelSize — Float. How far from text outline is bevel. Default is 8.</li>
+			<li>bevelOffset — Float. How far from text outline bevel starts. Default is 0.</li>
 			<li>bevelSegments — Integer. Number of bevel segments. Default is 3.</li>
 		</ul>
 		</p>

+ 1 - 1
docs/api/en/helpers/AxesHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/Box3Helper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/BoxHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/CameraHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/FaceNormalsHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 2 - 2
docs/api/en/helpers/GridHelper.html

@@ -8,8 +8,8 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:Line] &rarr;
-
+		[page:Object3D] &rarr; [page:Line] &rarr;
+		
 		<h1>[name]</h1>
 
 		<p class="desc">The GridHelper is an object to define grids. Grids are two-dimensional arrays of lines.</p>

+ 1 - 1
docs/api/en/helpers/PlaneHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:LineSegments] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr; [page:LineSegments] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/PointLightHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:Mesh] &rarr;
+		[page:Object3D] &rarr; [page:Mesh] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/PolarGridHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:Line] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr;
 
 		<h1>[name]</h1>
 

+ 1 - 1
docs/api/en/helpers/VertexNormalsHelper.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:Line] &rarr;
+		[page:Object3D] &rarr; [page:Line] &rarr;
 
 		<h1>[name]</h1>
 

+ 2 - 2
docs/api/en/materials/MeshToonMaterial.html

@@ -14,7 +14,7 @@
 
 		<div class="desc">An extension of the [page:MeshPhongMaterial] with toon shading.</div>
 
-		<!-- <iframe id="scene" src="scenes/material-browser.html#MeshStandardMaterial"></iframe>
+		<iframe id="scene" src="scenes/material-browser.html#MeshToonMaterial"></iframe>
 
 		<script>
 
@@ -30,7 +30,7 @@
 
 		}
 
-		</script> -->
+		</script>
 
 		<h2>Examples</h2>
 		[example:webgl_materials_variations_toon materials / variations / toon]<br />

+ 1 - 1
docs/api/en/materials/RawShaderMaterial.html

@@ -8,7 +8,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
-		[page:ShaderMaterial] &rarr;
+		[page:Material] &rarr; [page:ShaderMaterial] &rarr;
 
 		<h1>[name]</h1>
 

+ 2 - 2
docs/api/en/objects/LOD.html

@@ -72,7 +72,7 @@ scene.add( lod );
 		<h2>Methods</h2>
 		<p>See the base [page:Object3D] class for common methods.</p>
 
-		<h3>[method:null addLevel]( [param:Object3D object], [param:Float distance] )</h3>
+		<h3>[method:this addLevel]( [param:Object3D object], [param:Float distance] )</h3>
 		<p>
 		[page:Object3D object] - The [page:Object3D] to display at this level.<br />
 		[page:Float distance] - The distance at which to display this level of detail.<br /><br />
@@ -100,7 +100,7 @@ scene.add( lod );
 
 
 
-		<h3>[method:null toJSON]( meta )</h3>
+		<h3>[method:Object toJSON]( meta )</h3>
 		<p>
 		Create a JSON structure with details of this LOD object.
 		</p>

+ 9 - 0
docs/api/en/renderers/WebGLRenderer.html

@@ -92,6 +92,15 @@
 			Default is *true*.
 		</p>
 
+		<h3>[property:Boolean debug.checkShaderErrors]</h3>
+		<p>
+			If [page:.checkShaderErrors checkShaderErrors] is true, defines whether material shader programs are checked
+			for errors during compilation and linkage process. It may be useful to disable this check in production for performance gain.
+			It is strongly recommended to keep these checks enabled during development.
+			If the shader does not compile and link - it will not work and associated material will not render.
+			Default is *true*.
+		</p>
+
 		<h3>[property:Object capabilities]</h3>
 		<p>
 		An object containing details about the capabilities of the current [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext RenderingContext].<br />

+ 1 - 1
docs/api/zh/cameras/ArrayCamera.html

@@ -15,7 +15,7 @@
 		<p class="desc">
 
 			[name] 用于更加高效地使用一组已经预定义的摄像机来渲染一个场景。这将能够更好地提升VR场景的渲染性能。<br />
-			一个 [name] 的实例中总是包含着一组子摄像机,应当为每一个子摄像机定义*bound*(边界)这个属性,这一属性决定了由该子摄像机所渲染的视口区域的大小。
+			一个 [name] 的实例中总是包含着一组子摄像机,应当为每一个子摄像机定义*viewport*(边界)这个属性,这一属性决定了由该子摄像机所渲染的视口区域的大小。
 		</p>
 
 		<h2>示例</h2>

+ 2 - 2
docs/api/zh/materials/MeshToonMaterial.html

@@ -14,7 +14,7 @@
 
 		<div class="desc">[page:MeshPhongMaterial]卡通着色的扩展。</div>
 
-		<!-- <iframe id="scene" src="scenes/material-browser.html#MeshStandardMaterial"></iframe>
+		<iframe id="scene" src="scenes/material-browser.html#MeshToonMaterial"></iframe>
 
 		<script>
 
@@ -30,7 +30,7 @@
 
 		}
 
-		</script> -->
+		</script>
 
 		<h2>例子(Examples)</h2>
 		[example:webgl_materials_variations_toon materials / variations / toon]<br />

+ 2 - 2
docs/api/zh/objects/LOD.html

@@ -69,7 +69,7 @@ scene.add( lod );
 		<h2>方法</h2>
 		<p>请参阅其基类[page:Object3D]来查看共有方法。</p>
 
-		<h3>[method:null addLevel]( [param:Object3D object], [param:Float distance] )</h3>
+		<h3>[method:this addLevel]( [param:Object3D object], [param:Float distance] )</h3>
 		<p>
 		[page:Object3D object] —— 在这个层次中将要显示的[page:Object3D]。<br />
 		[page:Float distance] —— 将显示这一细节层次的距离。<br /><br />
@@ -96,7 +96,7 @@ scene.add( lod );
 
 
 
-		<h3>[method:null toJSON]( meta )</h3>
+		<h3>[method:Object toJSON]( meta )</h3>
 		<p>
 			使用这个方法,为LOD对象中的每个细节层次创建一个JSON结构。
 		</p>

+ 9 - 0
docs/api/zh/renderers/WebGLRenderer.html

@@ -79,6 +79,15 @@
 			默认是*true*
 		</p>
 
+		<h3>[property:Boolean debug.checkShaderErrors]</h3>
+		<p>
+			如果[page:.checkShaderErrors checkShaderErrors]为true,定义是否检查材质着色器程序
+			编译和链接过程中的错误。 禁用此检查生产以获得性能增益可能很有用。
+			强烈建议在开发期间保持启用这些检查。
+			如果着色器没有编译和链接 - 它将无法工作,并且相关材料将不会呈现。
+			默认是*true*
+		</p>
+
 		<h3>[property:Object capabilities]</h3>
 		<p>
             一个包含当前渲染环境([link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext RenderingContext])的功能细节的对象。<br />

+ 1 - 1
docs/examples/controls/OrbitControls.html

@@ -32,7 +32,7 @@ var scene = new THREE.Scene();
 
 var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
 
-var controls = new THREE.OrbitControls( camera );
+var controls = new THREE.OrbitControls( camera, renderer.domElement );
 
 //controls.update() must be called after any manual changes to the camera's transform
 camera.position.set( 0, 20, 100 );

+ 1 - 1
docs/examples/loaders/MTLLoader.html

@@ -94,7 +94,7 @@
 			[page:String path] — The path to the MTL file.
 		</p>
 		<p>
-			Parse a <em>mtl</em> text structure and return a [page:MTLLoaderMaterialCreator] instance.<br />
+			Parse a <em>mtl</em> text structure and return a [page:MTLLoaderMaterialCreator MTLLoader.MaterialCreator] instance.<br />
 		</p>
 
 

+ 3 - 3
docs/examples/loaders/OBJLoader.html

@@ -89,12 +89,12 @@
 		If an <em>obj</em> object or group uses multiple materials while declaring faces, geometry groups and an array of materials are used.
 		</p>
 
-		<h3>[method:OBJLoader setMaterials]( [param:Array materials] )</h3>
+		<h3>[method:OBJLoader setMaterials]( [param:MTLLoader.MaterialCreator materials] )</h3>
 		<p>
-		[page:Array materials] - Array of [page:Material Materials].
+		[page:MTLLoaderMaterialCreator MTLLoader.MaterialCreator materials] - A MaterialCreator instance.
 		</p>
 		<p>
-		Sets materials loaded by MTLLoader or any other supplier of an Array of [page:Material Materials].
+		Sets materials loaded by MTLLoader or any other supplier of a [page:MTLLoaderMaterialCreator MTLLoader.MaterialCreator].
 		</p>
 
 		<h3>[method:OBJLoader setPath]( [param:String path] )</h3>

+ 1 - 1
docs/examples/loaders/OBJLoader2.html

@@ -13,7 +13,7 @@
 
 		<p class="desc">A loader for loading a <em>.obj</em> resource.<br />
 			The <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">OBJ file format</a> is a simple data-format
-			that represents 3D geometry in a human redeable format as, the position of each vertex, the UV position of
+			that represents 3D geometry in a human readable format as, the position of each vertex, the UV position of
 			each texture coordinate vertex, vertex normals, and the faces that make each polygon defined as a list of
 			vertices, and texture vertices.
 		</p>

+ 78 - 62
docs/examples/quickhull/QuickHull.html

@@ -24,9 +24,9 @@
 
 		<h2>Properties</h2>
 
-		<h3>[property:Float tolerance]</h3>
+		<h3>[property:VertexList assigned]</h3>
 		<p>
-			The epsilon value that is used for internal comparative operations. The calculation of this value depends on the size of the geometry. Default is -1.
+			This [page:VertexList vertex list] holds all vertices that are assigned to a face. Default is an empty vertex list.
 		</p>
 
 		<h3>[property:Array faces]</h3>
@@ -39,9 +39,9 @@
 			This array holds the faces that are generated within a single iteration. Default is an empty array.
 		</p>
 
-		<h3>[property:VertexList assigned]</h3>
+		<h3>[property:Float tolerance]</h3>
 		<p>
-			This [page:VertexList vertex list] holds all vertices that are assigned to a face. Default is an empty vertex list.
+			The epsilon value that is used for internal comparative operations. The calculation of this value depends on the size of the geometry. Default is -1.
 		</p>
 
 		<h3>[property:VertexList unassigned]</h3>
@@ -56,20 +56,18 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:QuickHull setFromPoints]( [param:Array points] )</h3>
-		[page:Array points] - Array of [page:Vector3 Vector3s] that the resulting convex hull will contain.<br /><br />
-
-		<p>Computes to convex hull for the given array of points.</p>
-
-		<h3>[method:QuickHull setFromObject]( [param:Object3D object] )</h3>
-		[page:Object3D object] - [page:Object3D] to compute the convex hull of.<br /><br />
+		<h3>[method:HalfEdge addAdjoiningFace]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
+		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
+		[page:HalfEdge horizonEdge] - A single edge of the horizon.<br /><br />
 
-		<p>Computes the convex hull of an [page:Object3D] (including its children),
-		accounting for the world transforms of both the object and its childrens.</p>
+		<p>Creates a face with the vertices 'eyeVertex.point', 'horizonEdge.tail' and 'horizonEdge.head' in CCW order.
+			All the half edges are created in CCW order thus the face is always pointing outside the hull</p>
 
-		<h3>[method:QuickHull makeEmpty]()</h3>
+		<h3>[method:QuickHull addNewFaces]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
+		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
+		[page:HalfEdge horizon] - An array of half-edges that form the horizon.<br /><br />
 
-		<p>Makes this convex hull empty.</p>
+		<p>Adds 'horizon.length' faces to the hull, each face will be linked with the horizon opposite face and the face on the left/right.</p>
 
 		<h3>[method:QuickHull addVertexToFace]( [param:VertexNode vertex], [param:Face face]	)</h3>
 		[page:VertexNodeNode vertex] - The vertex to add.<br /><br />
@@ -77,16 +75,46 @@
 
 		<p>Adds a vertex to the 'assigned' list of vertices and assigns it to the given face.</p>
 
-		<h3>[method:QuickHull removeVertexFromFace]( [param:VertexNode vertex], [param:Face face]	)</h3>
-		[page:VertexNode vertex] - The vertex to remove.<br /><br />
-		[page:Face face] - The target face.<br /><br />
+		<h3>[method:QuickHull addVertexToHull]( [param:VertexNode eyeVertex] )</h3>
+		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
 
-		<p>Removes a vertex from the 'assigned' list of vertices and from the given face. It also makes sure that the link from 'face' to the first vertex it sees in 'assigned' is linked correctly after the removal.</p>
+		<p>Adds a vertex to the hull with the following algorithm
+			<ul>
+				<li>Compute the 'horizon' which is a chain of half edges. For an edge to belong to this group it must be the edge connecting a face that can see 'eyeVertex' and a face which cannot see 'eyeVertex'.</li>
+				<li>All the faces that can see 'eyeVertex' have its visible vertices removed from the assigned vertex list.</li>
+				<li>A new set of faces is created with each edge of the 'horizon' and 'eyeVertex'. Each face is connected with the opposite horizon face and the face on the left/right.</li>
+				<li>The vertices removed from all the visible faces are assigned to the new faces if possible.</li>
+			</ul>
+		</p>
 
-		<h3>[method:VertexNode removeAllVerticesFromFace]( [param:Face face]	)</h3>
-		[page:Face face] - The given face.<br /><br />
+		<h3>[method:QuickHull cleanup]()</h3>
 
-		<p>Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertext list.</p>
+		<p>Cleans up internal properties after computing the convex hull.</p>
+
+		<h3>[method:QuickHull compute]()</h3>
+
+		<p>Starts the execution of the quick hull algorithm.</p>
+
+		<h3>[method:Object computeExtremes]()</h3>
+
+		<p>Computes the extremes values (min/max vectors) which will be used to compute the inital hull.</p>
+
+		<h3>[method:QuickHull computeHorizon]( [param:Vector3 eyePoint], [param:HalfEdge crossEdge], [param:Face face], [param:Array horizon]	)</h3>
+		[page:Vector3 eyePoint] - The 3D-coordinates of a point.<br /><br />
+		[page:HalfEdge crossEdge] - The edge used to jump to the current face.<br /><br />
+		[page:Face face] - The current face being tested.<br /><br />
+		[page:Array horizon] - The edges that form part of the horizon in CCW order.<br /><br />
+
+		<p>Computes a chain of half edges in CCW order called the 'horizon'. For an edge to be part of the horizon it must join a face that can see 'eyePoint' and a face that cannot see 'eyePoint'.</p>
+
+		<h3>[method:QuickHull computeInitialHull]()</h3>
+
+		<p>Computes the initial simplex assigning to its faces all the points that are candidates to form part of the hull.</p>
+
+		<h3>[method:QuickHull containsPoint]( [param:Vector3 point] )</h3>
+		[page:Vector3 point] - A point in 3D space.<br /><br />
+
+		<p>Returns *true* if the given point is inside this convex hull.</p>
 
 		<h3>[method:QuickHull deleteFaceVertices]( [param:Face face], [param:Face absorbingFace]	)</h3>
 		[page:Face face] - The given face.<br /><br />
@@ -100,22 +128,20 @@
 			</ul>
 		</p>
 
-		<h3>[method:QuickHull resolveUnassignedPoints]( [param:Array newFaces]	)</h3>
-		[page:Face newFaces] - An array of new faces.<br /><br />
+		<h3>[method:Vector3 intersectRay]( [param:Ray ray], [param:Vector3 target] )</h3>
+		[page:Ray ray] - The given ray.<br /><br />
+		[page:Vector3 target] - The target vector representing the intersection point.<br /><br />
 
-		<p>Reassigns as many vertices as possible from the unassigned list to the new faces.</p>
+		<p>Performs a ray intersection test with this convext hull. If no intersection is found, *null* is returned.</p>
 
-		<h3>[method:Object computeExtremes]()</h3>
+		<h3>[method:Boolean intersectsRay]( [param:Ray ray] )</h3>
+		[page:Ray ray] - The given ray.<br /><br />
 
-		<p>Computes the extremes values (min/max vectors) which will be used to compute the inital hull.</p>
+		<p>Returns *true* if the given ray intersects with this convex hull.</p>
 
-		<h3>[method:QuickHull computeInitialHull]()</h3>
-
-		<p>Computes the initial simplex assigning to its faces all the points that are candidates to form part of the hull.</p>
-
-		<h3>[method:QuickHull reindexFaces]()</h3>
+		<h3>[method:QuickHull makeEmpty]()</h3>
 
-		<p>Removes inactive (e.g. deleted) faces from the internal face list.</p>
+		<p>Makes this convex hull empty.</p>
 
 		<h3>[method:VertexNode nextVertexToAdd]()</h3>
 
@@ -127,46 +153,36 @@
 			</ul>
 		</p>
 
-		<h3>[method:QuickHull computeHorizon]( [param:Vector3 eyePoint], [param:HalfEdge crossEdge], [param:Face face], [param:Array horizon]	)</h3>
-		[page:Vector3 eyePoint] - The 3D-coordinates of a point.<br /><br />
-		[page:HalfEdge crossEdge] - The edge used to jump to the current face.<br /><br />
-		[page:Face face] - The current face being tested.<br /><br />
-		[page:Array horizon] - The edges that form part of the horizon in CCW order.<br /><br />
+		<h3>[method:QuickHull reindexFaces]()</h3>
 
-		<p>Computes a chain of half edges in CCW order called the 'horizon'. For an edge to be part of the horizon it must join a face that can see 'eyePoint' and a face that cannot see 'eyePoint'.</p>
+		<p>Removes inactive (e.g. deleted) faces from the internal face list.</p>
 
-		<h3>[method:HalfEdge addAdjoiningFace]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
-		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
-		[page:HalfEdge horizonEdge] - A single edge of the horizon.<br /><br />
+		<h3>[method:VertexNode removeAllVerticesFromFace]( [param:Face face]	)</h3>
+		[page:Face face] - The given face.<br /><br />
 
-		<p>Creates a face with the vertices 'eyeVertex.point', 'horizonEdge.tail' and 'horizonEdge.head' in CCW order.
-			All the half edges are created in CCW order thus the face is always pointing outside the hull</p>
+		<p>Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertext list.</p>
 
-		<h3>[method:QuickHull addNewFaces]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
-		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
-		[page:HalfEdge horizon] - An array of half-edges that form the horizon.<br /><br />
+		<h3>[method:QuickHull removeVertexFromFace]( [param:VertexNode vertex], [param:Face face]	)</h3>
+		[page:VertexNode vertex] - The vertex to remove.<br /><br />
+		[page:Face face] - The target face.<br /><br />
 
-		<p>Adds 'horizon.length' faces to the hull, each face will be linked with the horizon opposite face and the face on the left/right.</p>
+		<p>Removes a vertex from the 'assigned' list of vertices and from the given face. It also makes sure that the link from 'face' to the first vertex it sees in 'assigned' is linked correctly after the removal.</p>
 
-		<h3>[method:QuickHull addVertexToHull]( [param:VertexNode eyeVertex] )</h3>
-		[page:VertexNode eyeVertex] - The vertex that is added to the hull.<br /><br />
+		<h3>[method:QuickHull resolveUnassignedPoints]( [param:Array newFaces]	)</h3>
+		[page:Face newFaces] - An array of new faces.<br /><br />
 
-		<p>Adds a vertex to the hull with the following algorithm
-			<ul>
-				<li>Compute the 'horizon' which is a chain of half edges. For an edge to belong to this group it must be the edge connecting a face that can see 'eyeVertex' and a face which cannot see 'eyeVertex'.</li>
-				<li>All the faces that can see 'eyeVertex' have its visible vertices removed from the assigned vertex list.</li>
-				<li>A new set of faces is created with each edge of the 'horizon' and 'eyeVertex'. Each face is connected with the opposite horizon face and the face on the left/right.</li>
-				<li>The vertices removed from all the visible faces are assigned to the new faces if possible.</li>
-			</ul>
-		</p>
+		<p>Reassigns as many vertices as possible from the unassigned list to the new faces.</p>
 
-		<h3>[method:QuickHull cleanup]()</h3>
+		<h3>[method:QuickHull setFromObject]( [param:Object3D object] )</h3>
+		[page:Object3D object] - [page:Object3D] to compute the convex hull of.<br /><br />
 
-		<p>Cleans up internal properties after computing the convex hull.</p>
+		<p>Computes the convex hull of an [page:Object3D] (including its children),
+		accounting for the world transforms of both the object and its childrens.</p>
 
-		<h3>[method:QuickHull compute]()</h3>
+		<h3>[method:QuickHull setFromPoints]( [param:Array points] )</h3>
+		[page:Array points] - Array of [page:Vector3 Vector3s] that the resulting convex hull will contain.<br /><br />
 
-		<p>Starts the execution of the quick hull algorithm.</p>
+		<p>Computes to convex hull for the given array of points.</p>
 
 		<h2>Source</h2>
 

BIN
docs/files/inconsolata.woff


+ 0 - 213
docs/index.css

@@ -1,213 +0,0 @@
-@font-face {
-	font-family: 'inconsolata';
-	src: url('files/inconsolata.woff') format('woff');
-	font-weight: normal;
-	font-style: normal;
-}
-
-* {
-	box-sizing: border-box;
-}
-
-html {
-	height: 100%;
-}
-
-body {
-	background-color: #ffffff;
-	margin: 0px;
-	height: 100%;
-	color: #555;
-	font-family: 'inconsolata';
-	font-size: 15px;
-	line-height: 18px;
-	overflow: hidden;
-}
-
-h1 {
-	margin-top: 30px;
-	margin-bottom: 40px;
-	font-size: 25px;
-	font-weight: normal;
-}
-
-h2 {
-	color: #454545;
-	font-size: 18px;
-	font-weight: normal;
-
-	margin-top: 20px;
-}
-
-h3 {
-	color: #666;
-	font-size: 16px;
-	font-weight: normal;
-
-	margin-top: 20px;
-}
-
-a {
-	color: #2194CE;
-	text-decoration: none;
-}
-
-#panel {
-	position: fixed;
-	left: 0px;
-	width: 260px;
-	height: 100%;
-	overflow: auto;
-	padding: 0px 20px 0px 20px;
-	background: #fafafa;
-}
-
-#panel ul {
-	list-style-type: none;
-	padding: 0px;
-}
-
-iframe {
-	position: absolute;
-	border: 0px;
-	left: 260px;
-	width: calc(100% - 260px);
-	height: 100%;
-	overflow: auto;
-}
-
-.filterBlock {
-	position: relative;
-}
-
-.filterBlock p {
-	margin: 0;
-}
-
-#filterInput {
-	width: 100%;
-	padding: 5px;
-	font-family: inherit;
-	font-size: 15px;
-	outline: none;
-	border: 1px solid #dedede;
-}
-
-#filterInput:focus{
-	border: 1px solid #2194CE;
-}
-
-#clearFilterButton {
-	position: absolute;
-	right: 6px;
-	top: 50%;
-	margin-top: -8px;
-	width: 16px;
-	height: 16px;
-	font-size: 14px;
-	color: grey;
-	text-align: center;
-	line-height: 0;
-	padding-top: 7px;
-	opacity: .5;
-}
-
-#clearFilterButton:hover {
-	opacity: 1;
-}
-
-.hidden {
-	display: none;
-}
-
-#panel li b {
-	font-weight: bold;
-}
-
-/* mobile */
-
-#expandButton {
-	display: none;
-	position: absolute;
-	right: 20px;
-	top: 12px;
-	width: 32px;
-	height: 32px;
-}
-
-#expandButton span {
-	height: 2px;
-	background-color: #2194CE;
-	width: 16px;
-	position: absolute;
-	left: 8px;
-	top: 10px;
-}
-
-#expandButton span:nth-child(1) {
-	top: 16px;
-}
-
-#expandButton span:nth-child(2) {
-	top: 22px;
-}
-
-@media all and ( max-width: 640px ) {
-
-	h1 {
-		margin-top: 20px;
-		margin-bottom: 20px;
-	}
-
-	#panel {
-		position: absolute;
-		left: 0;
-		top: 0;
-		height: 100%;
-		width: 100%;
-		right: 0;
-		z-index: 100;
-		overflow: hidden;
-		border-bottom: 1px solid #dedede;
-	}
-
-	#content {
-		position: absolute;
-		left: 0;
-		top: 120px;
-		right: 0;
-		bottom: 0;
-		font-size: 17px;
-		line-height: 22px;
-		padding-left: 20px;
-		overflow: auto;
-	}
-
-	#navigation {
-		position: absolute;
-		left: 0;
-		top: 90px;
-		right: 0;
-		bottom: 0;
-		font-size: 17px;
-		line-height: 22px;
-		overflow: auto;
-	}
-
-	iframe {
-		position: absolute;
-		left: 0;
-		top: 56px;
-		width: 100%;
-		height: calc(100% - 56px);
-	}
-
-	#expandButton {
-		display: block;
-	}
-
-	#panel.collapsed {
-		height: 56px;
-	}
-
-}

+ 28 - 32
docs/index.html

@@ -4,29 +4,32 @@
 		<meta charset="utf-8">
 		<title>three.js / documentation</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<link type="text/css" rel="stylesheet" href="index.css">
+		<link rel="shortcut icon" href="../files/favicon.ico" />
+		<link rel="stylesheet" type="text/css" href="../files/main.css">
+		<!-- console sandbox -->
+		<script src="../build/three.min.js" async defer></script>
 	</head>
 	<body>
 
 		<div id="panel" class="collapsed">
 
-			<h1><a href="http://threejs.org">three.js</a> / docs</h1>
+			<div id="header">
 
-			<a id="expandButton" href="#">
-				<span></span>
-				<span></span>
-				<span></span>
-			</a>
+				<h1><a href="http://threejs.org">three.js</a></h1>
 
-			<div class="filterBlock" >
-				<input type="text" id="filterInput" placeholder="Type to filter" autocorrect="off" autocapitalize="off" spellcheck="false">
-				<a href="#" id="clearFilterButton">x</a>
-			</div>
+				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
+
+				<div id="sections">
+					<span class="selected">docs</span> <a href="../examples/">examples</a>
+				</div>
+
+				<input type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false">
+
+				<select id="language">
+					<option value="en">en</option>
+					<option value="zh">zh</option>
+				</select>
 
-			<div style="text-align:center">
-				<br />
-				<a href="javascript:setLanguage('en')">en</a> |
-				<a href="javascript:setLanguage('zh')">zh</a>
 			</div>
 
 			<div id="content"></div>
@@ -65,9 +68,18 @@
 
 			//
 
+			var languageSelect = document.getElementById( 'language' );
+			languageSelect.value = language;
+			languageSelect.addEventListener( 'change', function ( event ) {
+
+				setLanguage( this.value );
+
+			} );
+
 			function setLanguage( value ) {
 
 				language = value;
+
 				createNavigation();
 				updateFilter();
 				autoChangeUrlLanguage( language );
@@ -76,9 +88,8 @@
 
 			var panel = document.getElementById( 'panel' );
 			var content = document.getElementById( 'content' );
-			var clearFilterButton = document.getElementById( 'clearFilterButton' );
 			var expandButton = document.getElementById( 'expandButton' );
-			var filterInput = document.getElementById( 'filterInput' );
+			var filterInput = document.getElementById( 'filter' );
 			var iframe = document.querySelector( 'iframe' );
 
 			var pageProperties = {};
@@ -106,18 +117,6 @@
 
 			};
 
-
-			// Functionality for filter clear button
-
-			clearFilterButton.onclick = function ( event ) {
-
-				event.preventDefault();
-
-				filterInput.value = '';
-				updateFilter();
-
-			};
-
 			// Activate content and title change on browser navigation
 
 			window.onpopstate = createNewIframe;
@@ -433,9 +432,6 @@
 
 		</script>
 
-		<!-- console sandbox -->
-		<script src="../build/three.min.js"></script>
-
 	</body>
 
 </html>

+ 65 - 4
docs/manual/en/introduction/Import-via-modules.html

@@ -62,11 +62,72 @@
 		...
 		</code>
 
-		<h2>Caveats</h2>
-
+		<h2>Importable Examples</h2>
+		<p>
+			The core of three.js is focused on the most important components of a 3D engine. Many other components like loaders or controls are part of the
+			examples directory. three.js ensures that these files are kept in sync with the core but users have to import them separately if they are required
+			for their project. However, most of these files are not modules which makes their usage in certain cases inconvenient. In order to address this issue,
+			we are working to provide all the examples as modules in the [link:https://github.com/mrdoob/three.js/tree/master/examples/jsm examples/jsm] directory.
+			If you install three.js via npm, you can import them like so:
+		</p>
+		<code>
+		import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+		</code>
+		<p>
+			The following examples files are already available as modules:
+			<ul>
+				<li>controls
+					<ul>
+						<li>MapControls</li>
+						<li>OrbitControls</li>
+						<li>TrackballControls</li>
+					</ul>
+				</li>
+				<li>exporters
+					<ul>
+						<li>ColladaExporter</li>
+						<li>GLTFExporter</li>
+						<li>MMDExporter</li>
+						<li>OBJExporter</li>
+						<li>PLYExporter</li>
+						<li>STLExporter</li>
+						<li>TypedGeometryExporter</li>
+					</ul>
+				</li>
+				<li>loaders
+					<ul>
+						<li>GLTFLoader</li>
+						<li>MTLLoader</li>
+						<li>OBJLoader</li>
+						<li>STLLoader</li>
+					</ul>
+				</li>
+				<li>pmrem
+					<ul>
+						<li>PMREMCubeUVPacker</li>
+						<li>PMREMGenerator</li>
+					</ul>
+				</li>
+				<li>utils
+					<ul>
+						<li>BufferGeometryUtils</li>
+						<li>GeometryUtils</li>
+						<li>MathUtils</li>
+						<li>SceneUtils</li>
+						<li>ShadowMapViewer</li>
+						<li>SkeletonUtils</li>
+						<li>TypedArrayUtils</li>
+						<li>UVsDebug</li>
+					</ul>
+				</li>
+			</ul>
+		</p>
 		<p>
-			Currently it's not possible to import the files within the "examples/js" directory in this way.
-			This is due to some of the files relying on global namespace pollution of THREE. For more information see <a href="https://github.com/mrdoob/three.js/issues/9562" target="_blank">Transform `examples/js` to support modules #9562</a>.
+			Note: When using code from the examples directory, it's important that all files match the version of
+			your three.js main file. For example it's no good approach to use *GLTFLoader* and *OrbitControls* from R96 together
+			with three.js R103. You can easily keep your files in sync by using the modules from the JSM directory. If the file
+			is not available as a module, you can still use third-party npm packages or convert the file to a module by yourself.
+			In both cases, ensure the code is compatible with your three.js main file.
 		</p>
 	</body>
 </html>

+ 6 - 0
docs/manual/en/introduction/Useful-links.html

@@ -33,6 +33,9 @@
 
 		<h3>Getting started with three.js</h3>
 		<ul>
+			<li>
+				[link:https://threejsfundamentals.org/threejs/lessons/threejs-fundamentals.html Three.js Fundamentals starting lesson]
+			</li>
 			<li>
 				[link:https://codepen.io/rachsmith/post/beginning-with-3d-webgl-pt-1-the-scene Beginning with 3D WebGL] by [link:https://codepen.io/rachsmith/ Rachel Smith].
 			</li>
@@ -43,6 +46,9 @@
 
 		<h3>More extensive / advanced articles and courses</h3>
 		<ul>
+			<li>
+				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
+			</li>
 			<li>
 				[link:http://blog.cjgammon.com/ Collection of tutorials] by [link:http://www.cjgammon.com/ CJ Gammon].
 			</li>

+ 58 - 25
docs/page.css

@@ -1,16 +1,25 @@
 @font-face {
-	font-family: 'inconsolata';
-	src: url('files/inconsolata.woff') format('woff');
+	font-family: 'RobotoMono';
+	src: local('RobotoMono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
+	font-weight: normal;
+	font-style: normal;
+}
+
+@font-face {
+	font-family: 'SF-Pro-Text';
+	src: local('SF-Pro-Text'), url('../files/SF-Pro-Text-Regular.otf');
 	font-weight: normal;
 	font-style: normal;
 }
 
 body {
-	margin: 30px 20px;
+	margin: 78px auto;
+	padding: 0px 24px;
+	max-width: 780px;
 	color: #555;
-	font-family: 'inconsolata';
-	font-size: 15px;
-	line-height: 18px;
+	font-family: 'SF-Pro-Text', sans-serif;
+	font-size: 16px;
+	line-height: 23px;
 	tab-size: 4;
 	overflow: auto;
 }
@@ -22,19 +31,18 @@ a {
 }
 
 h1 {
-	color: #333;
-	font-size: 25px;
+	color: #049EF4;
+	font-size: 32px;
 	font-weight: normal;
-
-	margin-top: 10px;
+	line-height: 42px;
 }
 
 h2 {
 	color: #4B0;
-	font-size: 18px;
-	font-weight: normal;
 
-	margin-top: 40px;
+	font-size: 22px;
+	font-weight: normal;
+	line-height: 31px;
 }
 
 h3 {
@@ -67,9 +75,6 @@ pre, code {
 
 code {
 	display: block;
-	width: -webkit-calc( 100% - 40px );
-	width: -moz-calc( 100% - 40px );
-	width: calc( 100% - 40px );
 	padding: 20px;
 	white-space: pre-wrap;
 	background-color: #f9f9f9;
@@ -104,19 +109,26 @@ strong {
 
 #button {
 	position: fixed;
-	top: 20px;
-	right: 20px;
+	bottom: 16px;
+	right: 16px;
+
 	padding: 8px;
-	color: #fff;
-	background-color: #555;
-	opacity: 0.5;
-}
+	border-radius: 50%;
+	margin-bottom: 0px; /* TODO div sets it to 20px */
 
-#button:hover {
-	cursor: pointer;
-	opacity: 1;
+	background-color: #dddddd;
+	opacity: 0.4;
 }
 
+	#button:hover {
+		cursor: pointer;
+		opacity: 1;
+	}
+
+	#button img {
+		display: block;
+	}
+
 a.permalink {
 	float: right;
 	margin-left: 5px;
@@ -141,3 +153,24 @@ sup, sub {
 sub {
 	top: 0.4em;
 }
+
+/* mobile */
+
+@media all and ( max-width: 640px ) {
+
+	body {
+		margin: 14px auto;
+		padding: 0px 14px;
+		font-size: 14px;
+		line-height: 22px;
+	}
+
+	h1 {
+		font-size: 26px;
+	}
+
+	h2 {
+		font-size: 18px;
+		line-height: 25px;
+	}
+}

+ 1 - 2
docs/page.js

@@ -97,8 +97,7 @@ function onDocumentLoad( event ) {
 
 	var button = document.createElement( 'div' );
 	button.id = 'button';
-	button.textContent = 'Edit';
-
+	button.innerHTML = '<img src="../files/ic_mode_edit_black_24dp.svg">';
 	button.addEventListener( 'click', function ( event ) {
 
 		window.open( 'https://github.com/mrdoob/three.js/blob/dev/docs/' + section + '/' + localizedPath + '.html' );

+ 4 - 2
docs/prettify/threejs.css

@@ -10,6 +10,8 @@ pre .dec, code .dec { color: #22c0c4; } /* decimal */
 
 
 pre.prettyprint, code.prettyprint {
-	background-color: #f9f9f9;
-	font-family: 'inconsolata';
+	background-color: #F5F5F5;
+	font-family: 'RobotoMono', monospace;
+	font-size: 14px;
+	line-height: 21px;
 }

+ 5 - 16
docs/scenes/bones-browser.html

@@ -3,22 +3,9 @@
 	<head>
 		<meta charset="utf-8">
 		<title>Three.js Bones Browser</title>
+		<link rel="shortcut icon" href="../../files/favicon.ico" />
+		<link rel="stylesheet" type="text/css" href="../../files/main.css">
 		<style>
-			@font-face {
-				font-family: 'inconsolata';
-				src: url('../files/inconsolata.woff') format('woff');
-				font-weight: normal;
-				font-style: normal;
-			}
-
-			body {
-				margin:0;
-				font-family: 'inconsolata';
-				font-size: 15px;
-				line-height: 18px;
-				overflow: hidden;
-			}
-
 			canvas { width: 100%; height: 100% }
 
 			#newWindow {
@@ -49,7 +36,10 @@
 			function initScene() {
 
 				gui = new dat.GUI();
+
 				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0x444444 );
+
 				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 200 );
 				camera.position.z = 30;
 				camera.position.y = 30;
@@ -57,7 +47,6 @@
 				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
-				renderer.setClearColor( 0x000000, 1 );
 				document.body.appendChild( renderer.domElement );
 
 				orbit = new THREE.OrbitControls( camera, renderer.domElement );

+ 5 - 16
docs/scenes/geometry-browser.html

@@ -3,22 +3,9 @@
 	<head>
 		<meta charset="utf-8">
 		<title>Three.js Geometry Browser</title>
+		<link rel="shortcut icon" href="../../files/favicon.ico" />
+		<link rel="stylesheet" type="text/css" href="../../files/main.css">
 		<style>
-			@font-face {
-				font-family: 'inconsolata';
-				src: url('../files/inconsolata.woff') format('woff');
-				font-weight: normal;
-				font-style: normal;
-			}
-
-			body {
-				margin:0;
-				font-family: 'inconsolata';
-				font-size: 15px;
-				line-height: 18px;
-				overflow: hidden;
-			}
-
 			canvas { width: 100%; height: 100% }
 
 			#newWindow {
@@ -46,14 +33,16 @@
 			document.getElementById( 'newWindow' ).href += window.location.hash;
 
 			var gui = new dat.GUI();
+
 			var scene = new THREE.Scene();
+			scene.background = new THREE.Color( 0x444444 );
+
 			var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
 			camera.position.z = 30;
 
 			var renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setSize( window.innerWidth, window.innerHeight );
-			renderer.setClearColor( 0x000000, 1 );
 			document.body.appendChild( renderer.domElement );
 
 			var orbit = new THREE.OrbitControls( camera, renderer.domElement );

+ 14 - 4
docs/scenes/js/geometry.js

@@ -860,6 +860,7 @@ var guis = {
 			bevelEnabled: false,
 			bevelThickness: 1,
 			bevelSize: 0.5,
+			bevelOffset: 0.0,
 			bevelSegments: 3
 		};
 
@@ -887,6 +888,7 @@ var guis = {
 					bevelEnabled: data.bevelEnabled,
 					bevelThickness: data.bevelThickness,
 					bevelSize: data.bevelSize,
+					bevelOffset: data.bevelOffset,
 					bevelSegments: data.bevelSegments
 				} );
 				geometry.center();
@@ -910,7 +912,8 @@ var guis = {
 		folder.add( data, 'weight', weights ).onChange( generateGeometry );
 		folder.add( data, 'bevelEnabled' ).onChange( generateGeometry );
 		folder.add( data, 'bevelThickness', 0.1, 3 ).onChange( generateGeometry );
-		folder.add( data, 'bevelSize', 0.1, 3 ).onChange( generateGeometry );
+		folder.add( data, 'bevelSize', 0, 3 ).onChange( generateGeometry );
+		folder.add( data, 'bevelOffset', -0.5, 1.5 ).onChange( generateGeometry );
 		folder.add( data, 'bevelSegments', 0, 8 ).step( 1 ).onChange( generateGeometry );
 
 		generateGeometry();
@@ -929,6 +932,7 @@ var guis = {
 			bevelEnabled: false,
 			bevelThickness: 1,
 			bevelSize: 0.5,
+			bevelOffset: 0.0,
 			bevelSegments: 3
 		};
 
@@ -956,6 +960,7 @@ var guis = {
 					bevelEnabled: data.bevelEnabled,
 					bevelThickness: data.bevelThickness,
 					bevelSize: data.bevelSize,
+					bevelOffset: data.bevelOffset,
 					bevelSegments: data.bevelSegments
 				} );
 				geometry.center();
@@ -979,7 +984,8 @@ var guis = {
 		folder.add( data, 'weight', weights ).onChange( generateGeometry );
 		folder.add( data, 'bevelEnabled' ).onChange( generateGeometry );
 		folder.add( data, 'bevelThickness', 0.1, 3 ).onChange( generateGeometry );
-		folder.add( data, 'bevelSize', 0.1, 3 ).onChange( generateGeometry );
+		folder.add( data, 'bevelSize', 0, 3 ).onChange( generateGeometry );
+		folder.add( data, 'bevelOffset', -0.5, 1.5 ).onChange( generateGeometry );
 		folder.add( data, 'bevelSegments', 0, 8 ).step( 1 ).onChange( generateGeometry );
 
 		generateGeometry();
@@ -1276,6 +1282,7 @@ var guis = {
 			bevelEnabled: true,
 			bevelThickness: 1,
 			bevelSize: 1,
+			bevelOffset: 0,
 			bevelSegments: 1
 		};
 
@@ -1302,7 +1309,8 @@ var guis = {
 		folder.add( data, 'steps', 1, 10 ).step( 1 ).onChange( generateGeometry );
 		folder.add( data, 'depth', 1, 20 ).onChange( generateGeometry );
 		folder.add( data, 'bevelThickness', 1, 5 ).step( 1 ).onChange( generateGeometry );
-		folder.add( data, 'bevelSize', 1, 5 ).step( 1 ).onChange( generateGeometry );
+		folder.add( data, 'bevelSize', 0, 5 ).step( 1 ).onChange( generateGeometry );
+		folder.add( data, 'bevelOffset', -4, 5 ).step( 1 ).onChange( generateGeometry );
 		folder.add( data, 'bevelSegments', 1, 5 ).step( 1 ).onChange( generateGeometry );
 
 		generateGeometry();
@@ -1317,6 +1325,7 @@ var guis = {
 			bevelEnabled: true,
 			bevelThickness: 1,
 			bevelSize: 1,
+			bevelOffset: 0,
 			bevelSegments: 1
 		};
 
@@ -1343,7 +1352,8 @@ var guis = {
 		folder.add( data, 'steps', 1, 10 ).step( 1 ).onChange( generateGeometry );
 		folder.add( data, 'depth', 1, 20 ).onChange( generateGeometry );
 		folder.add( data, 'bevelThickness', 1, 5 ).step( 1 ).onChange( generateGeometry );
-		folder.add( data, 'bevelSize', 1, 5 ).step( 1 ).onChange( generateGeometry );
+		folder.add( data, 'bevelSize', 0, 5 ).step( 1 ).onChange( generateGeometry );
+		folder.add( data, 'bevelOffset', -4, 5 ).step( 1 ).onChange( generateGeometry );
 		folder.add( data, 'bevelSegments', 1, 5 ).step( 1 ).onChange( generateGeometry );
 
 		generateGeometry();

+ 53 - 0
docs/scenes/js/material.js

@@ -166,11 +166,30 @@ var alphaMaps = ( function () {
 
 } )();
 
+var gradientMaps = ( function () {
+
+	var threeTone = textureLoader.load( '../../examples/textures/gradientMaps/threeTone.jpg' );
+	threeTone.minFilter = THREE.NearestFilter;
+	threeTone.magFilter = THREE.NearestFilter;
+
+	var fiveTone = textureLoader.load( '../../examples/textures/gradientMaps/fiveTone.jpg' );
+	fiveTone.minFilter = THREE.NearestFilter;
+	fiveTone.magFilter = THREE.NearestFilter;
+
+	return {
+		none: null,
+		threeTone: threeTone,
+		fiveTone: fiveTone
+	};
+
+} )();
+
 var envMapKeys = getObjectsKeys( envMaps );
 var diffuseMapKeys = getObjectsKeys( diffuseMaps );
 var roughnessMapKeys = getObjectsKeys( roughnessMaps );
 var matcapKeys = getObjectsKeys( matcaps );
 var alphaMapKeys = getObjectsKeys( alphaMaps );
+var gradientMapKeys = getObjectsKeys( gradientMaps );
 
 function generateVertexColors( geometry ) {
 
@@ -452,6 +471,25 @@ function guiMeshPhongMaterial( gui, mesh, material, geometry ) {
 
 }
 
+function guiMeshToonMaterial( gui, mesh, material ) {
+
+	var data = {
+		color: material.color.getHex(),
+		map: diffuseMapKeys[ 0 ],
+		gradientMap: gradientMapKeys[ 1 ],
+		alphaMap: alphaMapKeys[ 0 ]
+	};
+
+	var folder = gui.addFolder( 'THREE.MeshToonMaterial' );
+
+	folder.addColor( data, 'color' ).onChange( handleColorChange( material.color ) );
+
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'gradientMap', gradientMapKeys ).onChange( updateTexture( material, 'gradientMap', gradientMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
+
+}
+
 function guiMeshStandardMaterial( gui, mesh, material, geometry ) {
 
 	var data = {
@@ -566,6 +604,21 @@ function chooseFromHash( gui, mesh, geometry ) {
 
 			break;
 
+		case 'MeshToonMaterial' :
+
+			material = new THREE.MeshToonMaterial( { color: 0x2194CE, gradientMap: gradientMaps.threeTone } );
+			guiMaterial( gui, mesh, material, geometry );
+			guiMeshToonMaterial( gui, mesh, material, geometry );
+
+			// only use a single point light
+
+			lights[ 0 ].visible = false;
+			lights[ 2 ].visible = false;
+
+			return material;
+
+			break;
+
 		case 'MeshStandardMaterial' :
 
 			material = new THREE.MeshStandardMaterial( { color: 0x2194CE } );

+ 5 - 16
docs/scenes/material-browser.html

@@ -3,22 +3,9 @@
 	<head>
 		<meta charset="utf-8">
 		<title>Three.js Material Browser</title>
+		<link rel="shortcut icon" href="../../files/favicon.ico" />
+		<link rel="stylesheet" type="text/css" href="../../files/main.css">
 		<style>
-			@font-face {
-				font-family: 'inconsolata';
-				src: url('../files/inconsolata.woff') format('woff');
-				font-weight: normal;
-				font-style: normal;
-			}
-
-			body {
-				margin:0;
-				font-family: 'inconsolata';
-				font-size: 15px;
-				line-height: 18px;
-				overflow: hidden;
-			}
-
 			canvas { width: 100%; height: 100% }
 
 			#newWindow {
@@ -43,14 +30,16 @@
 			document.getElementById( 'newWindow' ).href += window.location.hash;
 
 			var gui = new dat.GUI();
+
 			var scene = new THREE.Scene();
+			scene.background = new THREE.Color( 0x444444 );
+
 			var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 10, 50 );
 			camera.position.z = 30;
 
 			var renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setSize( window.innerWidth, window.innerHeight );
-			renderer.setClearColor( 0x000000, 1 );
 			document.body.appendChild( renderer.domElement );
 
 			var ambientLight = new THREE.AmbientLight( 0x000000 );

+ 1 - 0
editor/index.html

@@ -6,6 +6,7 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link rel="apple-touch-icon" href="images/icon.png">
 		<link rel="manifest" href="manifest.json">
+		<link rel="shortcut icon" href="../files/favicon.ico" />
 	</head>
 	<body ontouchstart="">
 		<link rel="stylesheet" href="css/main.css">

+ 123 - 123
editor/js/Menubar.Add.js

@@ -37,135 +37,180 @@ Menubar.Add = function ( editor ) {
 
 	options.add( new UI.HorizontalRule() );
 
-	// Plane
+	// Box
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/plane' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/box' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.PlaneBufferGeometry( 1, 1, 1, 1 );
-		var material = new THREE.MeshStandardMaterial();
-		var mesh = new THREE.Mesh( geometry, material );
-		mesh.name = 'Plane';
+		var geometry = new THREE.BoxBufferGeometry( 1, 1, 1, 1, 1, 1 );
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
+		mesh.name = 'Box';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Box
+	// Circle
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/box' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/circle' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.BoxBufferGeometry( 1, 1, 1, 1, 1, 1 );
+		var geometry = new THREE.CircleBufferGeometry( 1, 8, 0, Math.PI * 2 );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Box';
+		mesh.name = 'Circle';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Circle
+	// Cylinder
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/circle' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/cylinder' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.CircleBufferGeometry( 1, 8, 0, Math.PI * 2 );
+		var geometry = new THREE.CylinderBufferGeometry( 1, 1, 1, 8, 1, false, 0, Math.PI * 2 );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Circle';
+		mesh.name = 'Cylinder';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Ring
+	// Icosahedron
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/ring' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/icosahedron' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.RingBufferGeometry( 0.5, 1, 8, 1, 0, Math.PI * 2 );
+		var geometry = new THREE.IcosahedronBufferGeometry( 1, 0 );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Ring';
+		mesh.name = 'Icosahedron';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Cylinder
+	// Lathe
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/cylinder' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/lathe' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.CylinderBufferGeometry( 1, 1, 1, 8, 1, false, 0, Math.PI * 2 );
-		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Cylinder';
+		var points = [
+			new THREE.Vector2( 0, 0 ),
+			new THREE.Vector2( 0.4, 0 ),
+			new THREE.Vector2( 0.35, 0.05 ),
+			new THREE.Vector2( 0.1, 0.075 ),
+			new THREE.Vector2( 0.08, 0.1 ),
+			new THREE.Vector2( 0.08, 0.4 ),
+			new THREE.Vector2( 0.1, 0.42 ),
+			new THREE.Vector2( 0.14, 0.48 ),
+			new THREE.Vector2( 0.2, 0.5 ),
+			new THREE.Vector2( 0.25, 0.54 ),
+			new THREE.Vector2( 0.3, 1.2 )
+		];
+
+		var geometry = new THREE.LatheBufferGeometry( points, 12, 0, Math.PI * 2 );
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial( { side: THREE.DoubleSide } ) );
+		mesh.name = 'Lathe';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Sphere
+	// Octahedron
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/sphere' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/octahedron' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.SphereBufferGeometry( 1, 8, 6, 0, Math.PI * 2, 0, Math.PI );
+		var geometry = new THREE.OctahedronBufferGeometry( 1, 0 );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Sphere';
+		mesh.name = 'Octahedron';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Icosahedron
+	// Plane
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/icosahedron' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/plane' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.IcosahedronBufferGeometry( 1, 0 );
+		var geometry = new THREE.PlaneBufferGeometry( 1, 1, 1, 1 );
+		var material = new THREE.MeshStandardMaterial();
+		var mesh = new THREE.Mesh( geometry, material );
+		mesh.name = 'Plane';
+
+		editor.execute( new AddObjectCommand( mesh ) );
+
+	} );
+	options.add( option )
+
+	// Ring
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/ring' ) );
+	option.onClick( function () {
+
+		var geometry = new THREE.RingBufferGeometry( 0.5, 1, 8, 1, 0, Math.PI * 2 );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Icosahedron';
+		mesh.name = 'Ring';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
-	// Octahedron
+	// Sphere
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/octahedron' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/sphere' ) );
 	option.onClick( function () {
 
-		var geometry = new THREE.OctahedronBufferGeometry( 1, 0 );
+		var geometry = new THREE.SphereBufferGeometry( 1, 8, 6, 0, Math.PI * 2, 0, Math.PI );
 		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
-		mesh.name = 'Octahedron';
+		mesh.name = 'Sphere';
 
 		editor.execute( new AddObjectCommand( mesh ) );
 
 	} );
 	options.add( option );
 
+	// Sprite
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/sprite' ) );
+	option.onClick( function () {
+
+		var sprite = new THREE.Sprite( new THREE.SpriteMaterial() );
+		sprite.name = 'Sprite';
+
+		editor.execute( new AddObjectCommand( sprite ) );
+
+	} );
+	options.add( option );
+
 	// Tetrahedron
 
 	var option = new UI.Row();
@@ -266,90 +311,40 @@ Menubar.Add = function ( editor ) {
 	options.add( option );
 	*/
 
-	// Lathe
-
-	var option = new UI.Row();
-	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/lathe' ) );
-	option.onClick( function () {
-
-		var points = [
-			new THREE.Vector2( 0, 0 ),
-			new THREE.Vector2( 0.4, 0 ),
-			new THREE.Vector2( 0.35, 0.05 ),
-			new THREE.Vector2( 0.1, 0.075 ),
-			new THREE.Vector2( 0.08, 0.1 ),
-			new THREE.Vector2( 0.08, 0.4 ),
-			new THREE.Vector2( 0.1, 0.42 ),
-			new THREE.Vector2( 0.14, 0.48 ),
-			new THREE.Vector2( 0.2, 0.5 ),
-			new THREE.Vector2( 0.25, 0.54 ),
-			new THREE.Vector2( 0.3, 1.2 )
-		];
-
-		var geometry = new THREE.LatheBufferGeometry( points, 12, 0, Math.PI * 2 );
-		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial( { side: THREE.DoubleSide } ) );
-		mesh.name = 'Lathe';
-
-		editor.execute( new AddObjectCommand( mesh ) );
-
-	} );
-	options.add( option );
-
-	// Sprite
-
-	var option = new UI.Row();
-	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/sprite' ) );
-	option.onClick( function () {
-
-		var sprite = new THREE.Sprite( new THREE.SpriteMaterial() );
-		sprite.name = 'Sprite';
-
-		editor.execute( new AddObjectCommand( sprite ) );
-
-	} );
-	options.add( option );
-
 	//
 
 	options.add( new UI.HorizontalRule() );
 
-	// PointLight
+	// AmbientLight
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/pointlight' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/ambientlight' ) );
 	option.onClick( function () {
 
-		var color = 0xffffff;
-		var intensity = 1;
-		var distance = 0;
+		var color = 0x222222;
 
-		var light = new THREE.PointLight( color, intensity, distance );
-		light.name = 'PointLight';
+		var light = new THREE.AmbientLight( color );
+		light.name = 'AmbientLight';
 
 		editor.execute( new AddObjectCommand( light ) );
 
 	} );
 	options.add( option );
 
-	// SpotLight
+	// DirectionalLight
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/spotlight' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/directionallight' ) );
 	option.onClick( function () {
 
 		var color = 0xffffff;
 		var intensity = 1;
-		var distance = 0;
-		var angle = Math.PI * 0.1;
-		var penumbra = 0;
 
-		var light = new THREE.SpotLight( color, intensity, distance, angle, penumbra );
-		light.name = 'SpotLight';
-		light.target.name = 'SpotLight Target';
+		var light = new THREE.DirectionalLight( color, intensity );
+		light.name = 'DirectionalLight';
+		light.target.name = 'DirectionalLight Target';
 
 		light.position.set( 5, 10, 7.5 );
 
@@ -358,59 +353,64 @@ Menubar.Add = function ( editor ) {
 	} );
 	options.add( option );
 
-	// DirectionalLight
+	// HemisphereLight
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/directionallight' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/hemispherelight' ) );
 	option.onClick( function () {
 
-		var color = 0xffffff;
+		var skyColor = 0x00aaff;
+		var groundColor = 0xffaa00;
 		var intensity = 1;
 
-		var light = new THREE.DirectionalLight( color, intensity );
-		light.name = 'DirectionalLight';
-		light.target.name = 'DirectionalLight Target';
+		var light = new THREE.HemisphereLight( skyColor, groundColor, intensity );
+		light.name = 'HemisphereLight';
 
-		light.position.set( 5, 10, 7.5 );
+		light.position.set( 0, 10, 0 );
 
 		editor.execute( new AddObjectCommand( light ) );
 
 	} );
 	options.add( option );
 
-	// HemisphereLight
+	// PointLight
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/hemispherelight' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/pointlight' ) );
 	option.onClick( function () {
 
-		var skyColor = 0x00aaff;
-		var groundColor = 0xffaa00;
+		var color = 0xffffff;
 		var intensity = 1;
+		var distance = 0;
 
-		var light = new THREE.HemisphereLight( skyColor, groundColor, intensity );
-		light.name = 'HemisphereLight';
-
-		light.position.set( 0, 10, 0 );
+		var light = new THREE.PointLight( color, intensity, distance );
+		light.name = 'PointLight';
 
 		editor.execute( new AddObjectCommand( light ) );
 
 	} );
 	options.add( option );
 
-	// AmbientLight
+	// SpotLight
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/ambientlight' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/spotlight' ) );
 	option.onClick( function () {
 
-		var color = 0x222222;
+		var color = 0xffffff;
+		var intensity = 1;
+		var distance = 0;
+		var angle = Math.PI * 0.1;
+		var penumbra = 0;
 
-		var light = new THREE.AmbientLight( color );
-		light.name = 'AmbientLight';
+		var light = new THREE.SpotLight( color, intensity, distance, angle, penumbra );
+		light.name = 'SpotLight';
+		light.target.name = 'SpotLight Target';
+
+		light.position.set( 5, 10, 7.5 );
 
 		editor.execute( new AddObjectCommand( light ) );
 
@@ -421,30 +421,30 @@ Menubar.Add = function ( editor ) {
 
 	options.add( new UI.HorizontalRule() );
 
-	// PerspectiveCamera
+	// OrthographicCamera
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/perspectivecamera' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/orthographiccamera' ) );
 	option.onClick( function () {
 
-		var camera = new THREE.PerspectiveCamera();
-		camera.name = 'PerspectiveCamera';
+		var camera = new THREE.OrthographicCamera();
+		camera.name = 'OrthographicCamera';
 
 		editor.execute( new AddObjectCommand( camera ) );
 
 	} );
 	options.add( option );
 
-	// OrthographicCamera
+	// PerspectiveCamera
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
-	option.setTextContent( strings.getKey( 'menubar/add/orthographiccamera' ) );
+	option.setTextContent( strings.getKey( 'menubar/add/perspectivecamera' ) );
 	option.onClick( function () {
 
-		var camera = new THREE.OrthographicCamera();
-		camera.name = 'OrthographicCamera';
+		var camera = new THREE.PerspectiveCamera();
+		camera.name = 'PerspectiveCamera';
 
 		editor.execute( new AddObjectCommand( camera ) );
 

+ 62 - 0
editor/js/Menubar.Edit.js

@@ -199,6 +199,68 @@ Menubar.Edit = function ( editor ) {
 	} );
 	options.add( option );
 
+	options.add( new UI.HorizontalRule() );
+
+	// Set textures to sRGB. See #15903
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/edit/fixcolormaps' ) );
+	option.onClick( function () {
+
+		editor.scene.traverse( fixColorMap );
+
+	} );
+	options.add( option );
+
+	var colorMaps = [ 'map', 'envMap', 'emissiveMap' ];
+
+	function fixColorMap( obj ) {
+
+		var material = obj.material;
+
+		if ( material !== undefined ) {
+
+			if ( Array.isArray( material ) === true ) {
+
+				for ( var i = 0; i < material.length; i ++ ) {
+
+					fixMaterial( material[ i ] );
+
+				}
+
+			} else {
+
+				fixMaterial( material );
+
+			}
+
+			editor.signals.sceneGraphChanged.dispatch();
+
+		}
+
+	}
+
+	function fixMaterial( material ) {
+
+		var needsUpdate = material.needsUpdate;
+
+		for ( var i = 0; i < colorMaps.length; i ++ ) {
+
+			var map = material[ colorMaps[ i ] ];
+
+			if ( map ) {
+
+				map.encoding = THREE.sRGBEncoding;
+				needsUpdate = true;
+
+			}
+
+		}
+
+		material.needsUpdate = needsUpdate;
+
+	}
 
 	return container;
 

+ 1 - 4
editor/js/Menubar.File.js

@@ -393,14 +393,11 @@ Menubar.File = function ( editor ) {
 	//
 
 	var link = document.createElement( 'a' );
-	link.style.display = 'none';
-	document.body.appendChild( link ); // Firefox workaround, see #6594
-
 	function save( blob, filename ) {
 
 		link.href = URL.createObjectURL( blob );
 		link.download = filename || 'data.json';
-		link.click();
+		link.dispatchEvent( new MouseEvent( 'click' ) );
 
 		// URL.revokeObjectURL( url ); breaks Firefox...
 

+ 25 - 3
editor/js/Sidebar.Material.js

@@ -289,7 +289,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialMapRow = new UI.Row();
 	var materialMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialMap = new UI.Texture().onChange( update );
+	var materialMap = new UI.Texture().onChange( updateMaterial );
 
 	materialMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/map' ) ).setWidth( '90px' ) );
 	materialMapRow.add( materialMapEnabled );
@@ -401,7 +401,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialEnvMapRow = new UI.Row();
 	var materialEnvMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialEnvMap = new UI.Texture( THREE.SphericalReflectionMapping ).onChange( update );
+	var materialEnvMap = new UI.Texture( THREE.SphericalReflectionMapping ).onChange( updateMaterial );
 	var materialReflectivity = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
 
 	materialEnvMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/envmap' ) ).setWidth( '90px' ) );
@@ -441,7 +441,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialEmissiveMapRow = new UI.Row();
 	var materialEmissiveMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialEmissiveMap = new UI.Texture().onChange( update );
+	var materialEmissiveMap = new UI.Texture().onChange( updateMaterial );
 
 	materialEmissiveMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/emissivemap' ) ).setWidth( '90px' ) );
 	materialEmissiveMapRow.add( materialEmissiveMapEnabled );
@@ -1061,6 +1061,28 @@ Sidebar.Material = function ( editor ) {
 
 	}
 
+	function updateMaterial( texture ) {
+
+		if ( texture !== null ) {
+
+			if ( texture.encoding !== THREE.sRGBEncoding ) {
+
+				texture.encoding = THREE.sRGBEncoding;
+				var object = currentObject;
+				if ( object !== null ) {
+
+					object.material.needsUpdate = true;
+
+				}
+
+			}
+
+		}
+
+		update();
+
+	}
+
 	//
 
 	function setRowVisibility() {

+ 3 - 3
editor/js/Sidebar.Settings.js

@@ -16,8 +16,8 @@ Sidebar.Settings = function ( editor ) {
 	// language
 
 	var options = {
-		'en': 'English',
-		'zh': '中文'
+		en: 'English',
+		zh: '中文'
 	};
 
 	var languageRow = new UI.Row();
@@ -72,7 +72,7 @@ Sidebar.Settings = function ( editor ) {
 	themeRow.add( new UI.Text( strings.getKey( 'sidebar/settings/theme' ) ).setWidth( '90px' ) );
 	themeRow.add( theme );
 
-	container.add( themeRow );	
+	container.add( themeRow );
 
 	container.add( new Sidebar.Settings.Shortcuts( editor ) );
 	container.add( new Sidebar.Settings.Viewport( editor ) );

+ 28 - 5
editor/js/Strings.js

@@ -31,6 +31,7 @@ var Strings = function ( config ) {
 			'menubar/edit/clone': 'Clone',
 			'menubar/edit/delete': 'Delete (Del)',
 			'menubar/edit/minify_shaders': 'Minify Shaders',
+			'menubar/edit/fixcolormaps': 'Fix Color Maps',
 
 			'menubar/add': 'Add',
 			'menubar/add/group': 'Group',
@@ -143,7 +144,7 @@ var Strings = function ( config ) {
 			'sidebar/geometry/octahedron_geometry/radius': 'Radius',
 			'sidebar/geometry/octahedron_geometry/detail': 'Detail',
 
-      'sidebar/geometry/tetrahedron_geometry/radius': 'Radius',
+			'sidebar/geometry/tetrahedron_geometry/radius': 'Radius',
 			'sidebar/geometry/tetrahedron_geometry/detail': 'Detail',
 
 			'sidebar/geometry/lathe_geometry/segments': 'Segments',
@@ -156,10 +157,10 @@ var Strings = function ( config ) {
 			'sidebar/geometry/plane_geometry/widthsegments': 'Width segments',
 			'sidebar/geometry/plane_geometry/heightsegments': 'Height segments',
 
-			'sidebar/geometry/ring_geometry/innerRadius': 'Inner Radius',
-			'sidebar/geometry/ring_geometry/outerRadius': 'Outer Radius',
-			'sidebar/geometry/ring_geometry/thetaSegments': 'Theta Segments',
-			'sidebar/geometry/ring_geometry/phiSegments': 'Phi Segments',
+			'sidebar/geometry/ring_geometry/innerRadius': 'Inner radius',
+			'sidebar/geometry/ring_geometry/outerRadius': 'Outer radius',
+			'sidebar/geometry/ring_geometry/thetaSegments': 'Theta segments',
+			'sidebar/geometry/ring_geometry/phiSegments': 'Phi segments',
 			'sidebar/geometry/ring_geometry/thetastart': 'Theta start',
 			'sidebar/geometry/ring_geometry/thetalength': 'Theta length',
 
@@ -312,6 +313,7 @@ var Strings = function ( config ) {
 			'menubar/edit/clone': '拷贝',
 			'menubar/edit/delete': '删除 (Del)',
 			'menubar/edit/minify_shaders': '压缩着色器',
+			'menubar/edit/fixcolormaps': '修复颜色贴图',
 
 			'menubar/add': '添加',
 			'menubar/add/group': '组',
@@ -319,8 +321,11 @@ var Strings = function ( config ) {
 			'menubar/add/box': '正方体',
 			'menubar/add/circle': '圆',
 			'menubar/add/cylinder': '圆柱体',
+			'menubar/add/ring': '环',
 			'menubar/add/sphere': '球体',
 			'menubar/add/icosahedron': '二十面体',
+			'menubar/add/octahedron': '八面体',
+			'menubar/add/tetrahedron': '四面体',
 			'menubar/add/torus': '圆环体',
 			'menubar/add/torusknot': '环面纽结体',
 			'menubar/add/tube': '管',
@@ -332,6 +337,7 @@ var Strings = function ( config ) {
 			'menubar/add/hemispherelight': '半球光',
 			'menubar/add/ambientlight': '环境光',
 			'menubar/add/perspectivecamera': '透视相机',
+			'menubar/add/orthographiccamera': '正交相机',
 
 			'menubar/status/autosave': '自动保存',
 
@@ -362,6 +368,10 @@ var Strings = function ( config ) {
 			'sidebar/object/rotation': '旋转',
 			'sidebar/object/scale': '缩放',
 			'sidebar/object/fov': '视角',
+			'sidebar/object/left': '左',
+			'sidebar/object/right': '右',
+			'sidebar/object/top': '上',
+			'sidebar/object/bottom': '下',
 			'sidebar/object/near': '近点',
 			'sidebar/object/far': '远点',
 			'sidebar/object/intensity': '强度',
@@ -413,6 +423,12 @@ var Strings = function ( config ) {
 			'sidebar/geometry/icosahedron_geometry/radius': '半径',
 			'sidebar/geometry/icosahedron_geometry/detail': '面片分段',
 
+			'sidebar/geometry/octahedron_geometry/radius': '半径',
+			'sidebar/geometry/octahedron_geometry/detail': '面片分段',
+
+			'sidebar/geometry/tetrahedron_geometry/radius': '半径',
+			'sidebar/geometry/tetrahedron_geometry/detail': '面片分段',
+
 			'sidebar/geometry/lathe_geometry/segments': '分段',
 			'sidebar/geometry/lathe_geometry/phistart': '经度起点',
 			'sidebar/geometry/lathe_geometry/philength': '经度长度',
@@ -423,6 +439,13 @@ var Strings = function ( config ) {
 			'sidebar/geometry/plane_geometry/widthsegments': '宽度分段',
 			'sidebar/geometry/plane_geometry/heightsegments': '长度分段',
 
+			'sidebar/geometry/ring_geometry/innerRadius': '内半径',
+			'sidebar/geometry/ring_geometry/outerRadius': '外半径',
+			'sidebar/geometry/ring_geometry/thetaSegments': '弧度分段',
+			'sidebar/geometry/ring_geometry/phiSegments': '经度分段',
+			'sidebar/geometry/ring_geometry/thetastart': '弧度起点',
+			'sidebar/geometry/ring_geometry/thetalength': '弧度长度',
+
 			'sidebar/geometry/sphere_geometry/radius': '半径',
 			'sidebar/geometry/sphere_geometry/widthsegments': '宽度分段',
 			'sidebar/geometry/sphere_geometry/heightsegments': '长度分段',

+ 20 - 3
editor/js/libs/ui.three.js

@@ -64,7 +64,7 @@ UI.Texture = function ( mapping ) {
 
 					scope.setValue( texture );
 
-					if ( scope.onChangeCallback ) scope.onChangeCallback();
+					if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
 
 				}, false );
 
@@ -75,7 +75,7 @@ UI.Texture = function ( mapping ) {
 				reader.addEventListener( 'load', function ( event ) {
 
 					var image = document.createElement( 'img' );
-					image.addEventListener( 'load', function( event ) {
+					image.addEventListener( 'load', function ( event ) {
 
 						var texture = new THREE.Texture( this, mapping );
 						texture.sourceFile = file.name;
@@ -84,7 +84,7 @@ UI.Texture = function ( mapping ) {
 
 						scope.setValue( texture );
 
-						if ( scope.onChangeCallback ) scope.onChangeCallback();
+						if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
 
 					}, false );
 
@@ -161,6 +161,19 @@ UI.Texture.prototype.setValue = function ( texture ) {
 
 };
 
+UI.Texture.prototype.setEncoding = function ( encoding ) {
+
+	var texture = this.getValue();
+	if ( texture !== null ) {
+
+		texture.encoding = encoding;
+
+	}
+
+	return this;
+
+};
+
 UI.Texture.prototype.onChange = function ( callback ) {
 
 	this.onChangeCallback = callback;
@@ -188,11 +201,13 @@ UI.Outliner = function ( editor ) {
 	dom.addEventListener( 'keydown', function ( event ) {
 
 		switch ( event.keyCode ) {
+
 			case 38: // up
 			case 40: // down
 				event.preventDefault();
 				event.stopPropagation();
 				break;
+
 		}
 
 	}, false );
@@ -201,12 +216,14 @@ UI.Outliner = function ( editor ) {
 	dom.addEventListener( 'keyup', function ( event ) {
 
 		switch ( event.keyCode ) {
+
 			case 38: // up
 				scope.selectIndex( scope.selectedIndex - 1 );
 				break;
 			case 40: // down
 				scope.selectIndex( scope.selectedIndex + 1 );
 				break;
+
 		}
 
 	}, false );

+ 1 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r103
+// r104
 
 const staticAssets = [
 	'./',

+ 2 - 2
examples/css2d_label.html

@@ -64,8 +64,6 @@
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
 				camera.position.set( 10, 5, 20 );
 
-				var controls = new THREE.OrbitControls( camera );
-
 				scene = new THREE.Scene();
 
 				var dirLight = new THREE.DirectionalLight( 0xffffff );
@@ -128,6 +126,8 @@
 				labelRenderer.domElement.style.top = 0;
 				document.body.appendChild( labelRenderer.domElement );
 
+				var controls = new THREE.OrbitControls( camera, labelRenderer.domElement );
+
 			}
 
 			function animate() {

+ 2 - 2
examples/css3d_orthographic.html

@@ -53,8 +53,6 @@
 
 				camera.position.set( - 200, 200, 200 );
 
-				var controls = new THREE.OrbitControls( camera );
-
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0xf0f0f0 );
 
@@ -104,6 +102,8 @@
 				renderer2.domElement.style.top = 0;
 				document.body.appendChild( renderer2.domElement );
 
+				var controls = new THREE.OrbitControls( camera, renderer2.domElement );
+
 				function createPlane( width, height, cssColor, pos, rot ) {
 
 					var element = document.createElement( 'div' );

+ 2 - 2
examples/css3d_sandbox.html

@@ -52,8 +52,6 @@
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
 				camera.position.set( 200, 200, 200 );
 
-				controls = new THREE.TrackballControls( camera );
-
 				scene = new THREE.Scene();
 				scene.background = new THREE.Color( 0xf0f0f0 );
 
@@ -104,6 +102,8 @@
 				renderer2.domElement.style.top = 0;
 				document.body.appendChild( renderer2.domElement );
 
+				controls = new THREE.TrackballControls( camera, renderer2.domElement );
+
 			}
 
 			function animate() {

+ 1 - 1
examples/css3d_youtube.html

@@ -76,7 +76,7 @@
 				group.add( new Element( '9ubytEsCaS0', - 240, 0, 0, - Math.PI / 2 ) );
 				scene.add( group );
 
-				controls = new THREE.TrackballControls( camera );
+				controls = new THREE.TrackballControls( camera, renderer.domElement );
 				controls.rotateSpeed = 4;
 
 				window.addEventListener( 'resize', onWindowResize, false );

+ 5 - 3
examples/files.js

@@ -60,7 +60,7 @@ var files = {
 		"webgl_kinect",
 		"webgl_layers",
 		"webgl_lensflares",
-		"webgl_lightningstrike",
+		"webgl_lightprobe",
 		"webgl_lights_hemisphere",
 		"webgl_lights_physical",
 		"webgl_lights_pointlights",
@@ -68,7 +68,6 @@ var files = {
 		"webgl_lights_spotlight",
 		"webgl_lights_spotlights",
 		"webgl_lights_rectarealight",
-		"webgl_lightshafts",
 		"webgl_lines_colors",
 		"webgl_lines_dashed",
 		"webgl_lines_fat",
@@ -273,7 +272,8 @@ var files = {
 		"webgl_postprocessing_sobel",
 		"webgl_postprocessing_ssao",
 		"webgl_postprocessing_taa",
-		"webgl_postprocessing_unreal_bloom"
+		"webgl_postprocessing_unreal_bloom",
+		"webgl_postprocessing_unreal_bloom_selective"
 	],
 	"webgl / advanced": [
 		"webgl_buffergeometry",
@@ -305,6 +305,8 @@ var files = {
 		"webgl_gpgpu_water",
 		"webgl_gpgpu_protoplanet",
 		"webgl_gpu_particle_system",
+		"webgl_lightningstrike",
+		"webgl_lightshafts",
 		"webgl_materials_modified",
 		"webgl_raymarching_reflect",
 		"webgl_shadowmap_pcss",

BIN
examples/files/inconsolata.woff


+ 26 - 221
examples/index.html

@@ -1,90 +1,16 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js / examples</title>
 		<meta charset="utf-8">
+		<title>three.js / examples</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link rel="shortcut icon" href="../files/favicon.ico" />
+		<link rel="stylesheet" type="text/css" href="../files/main.css">
 		<style>
-
-			@font-face {
-				font-family: 'inconsolata';
-				src: url('files/inconsolata.woff') format('woff');
-				font-weight: normal;
-				font-style: normal;
-			}
-
-			* {
-				box-sizing: border-box;
-			}
-
-			html {
-				height: 100%;
-			}
-
-			body {
-				background-color: #ffffff;
-				margin: 0px;
-				height: 100%;
-				color: #555;
-				font-family: 'inconsolata';
-				font-size: 15px;
-				line-height: 18px;
-				overflow: hidden;
-			}
-
-			h1 {
-				margin-top: 30px;
-				margin-bottom: 40px;
-				margin-left: 20px;
-				font-size: 25px;
-				font-weight: normal;
-			}
-
-			h2 {
-				font-size: 20px;
-				font-weight: normal;
-			}
-
-			a {
-				color: #2194CE;
-				text-decoration: none;
-			}
-
-			#panel {
-				position: fixed;
-				left: 0px;
-				width: 310px;
-				height: 100%;
-				overflow: auto;
-				background: #fafafa;
-			}
-
-			#panel #content {
-				padding: 0px 20px 20px 20px;
-			}
-
 			#panel #content .link {
-				color: #2194CE;
+				display: block;
 				text-decoration: none;
 				cursor: pointer;
-				display: block;
-			}
-
-			#panel #content .selected {
-				color: #ff0000;
-			}
-
-			#panel #content .link:hover {
-				text-decoration: underline;
-			}
-
-			#viewer {
-				position: absolute;
-				border: 0px;
-				left: 310px;
-				width: calc(100% - 310px);
-				height: 100%;
-				overflow: auto;
 			}
 
 			#viewSrcButton {
@@ -101,142 +27,30 @@
 				cursor: pointer;
 				opacity: 1;
 			}
+		</style>
+	</head>
+	<body>
 
-			.filterBlock{
-				margin: 20px;
-				position: relative;
-			}
-
-			.filterBlock p {
-				margin: 0;
-			}
-
-			#filterInput {
-				width: 100%;
-				padding: 5px;
-				font-family: inherit;
-				font-size: 15px;
-				outline: none;
-				border: 1px solid #dedede;
-			}
-
-			#filterInput:focus{
-				border: 1px solid #2194CE;
-			}
-
-			#clearFilterButton {
-				position: absolute;
-				right: 6px;
-				top: 50%;
-				margin-top: -8px;
-				width: 16px;
-				height: 16px;
-				font-size: 14px;
-				color: grey;
-				text-align: center;
-				line-height: 0;
-				padding-top: 7px;
-				opacity: .5;
-			}
-
-			#clearFilterButton:hover {
-				opacity: 1;
-			}
-
-			.filtered {
-				display: none !important;
-			}
-
-			#panel li b {
-				font-weight: bold;
-			}
-
-			/* mobile */
-
-			#expandButton {
-				display: none;
-				position: absolute;
-				right: 20px;
-				top: 12px;
-				width: 32px;
-				height: 32px;
-			}
+		<div id="panel">
 
-			#expandButton span {
-				height: 2px;
-				background-color: #2194CE;
-				width: 16px;
-				position: absolute;
-				left: 8px;
-				top: 10px;
-			}
+			<div id="header">
 
-			#expandButton span:nth-child(1) {
-				top: 16px;
-			}
+				<h1><a href="http://threejs.org">three.js</a></h1>
 
-			#expandButton span:nth-child(2) {
-				top: 22px;
-			}
+				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
 
-			@media all and ( max-width: 640px ) {
-				h1{
-					margin-top: 20px;
-					margin-bottom: 20px;
-				}
-				#panel{
-					position: absolute;
-					left: 0;
-					top: 0;
-					height: 480px;
-					width: 100%;
-					right: 0;
-					z-index: 100;
-					overflow: hidden;
-					border-bottom: 1px solid #dedede;
-				}
-				#content{
-					position: absolute;
-					left: 0;
-					top: 90px;
-					right: 0;
-					bottom: 0;
-					font-size: 17px;
-					line-height: 22px;
-					overflow: auto;
-				}
-				#viewer{
-					position: absolute;
-					left: 0;
-					top: 56px;
-					width: 100%;
-					height: calc(100% - 56px);
-				}
-				#expandButton{
-					display: block;
-				}
-				#panel.collapsed{
-					height: 56px;
-				}
-			}
+				<div id="sections">
+					<a href="../docs/">docs</a> <span class="selected">examples</span>
+				</div>
 
-		</style>
-	</head>
-	<body>
+				<input type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false"/>
 
-		<div id="panel">
-			<h1><a href="http://threejs.org">three.js</a> / examples</h1>
-			<a id="expandButton" href="#">
-				<span></span>
-				<span></span>
-				<span></span>
-			</a>
-			<div class="filterBlock" >
-				<input type="text" id="filterInput" placeholder="Type to filter" autocorrect="off" autocapitalize="off" spellcheck="false"/>
-				<a href="#" id="clearFilterButton" >x</a>
 			</div>
+
 			<div id="content"></div>
+
 		</div>
+
 		<iframe id="viewer" name="viewer" allowfullscreen allowvr onmousewheel=""></iframe>
 
 		<script src="files.js"></script>
@@ -255,8 +69,7 @@
 		var content = document.getElementById( 'content' );
 		var viewer = document.getElementById( 'viewer' );
 
-		var filterInput = document.getElementById( 'filterInput' );
-		var clearFilterButton = document.getElementById( 'clearFilterButton' );
+		var filterInput = document.getElementById( 'filter' );
 
 		var expandButton = document.getElementById( 'expandButton' );
 		expandButton.addEventListener( 'click', function ( event ) {
@@ -351,7 +164,7 @@
 			// Reveal "View source" button and set attributes to this example
 			viewSrcButton.style.display = '';
 			viewSrcButton.href = 'https://github.com/mrdoob/three.js/blob/master/examples/' + selected + '.html';
-			viewSrcButton.title = 'View source code for ' + getName(selected) + ' on GitHub';
+			viewSrcButton.title = 'View source code for ' + getName( selected ) + ' on GitHub';
 
 		}
 
@@ -369,14 +182,6 @@
 
 		} );
 
-		clearFilterButton.addEventListener( 'click', function( e ) {
-
-			filterInput.value = '';
-			updateFilter();
-			e.preventDefault();
-
-		} );
-
 		function updateFilter() {
 
 			var v = filterInput.value;
@@ -413,7 +218,7 @@
 
 			if ( res && res.length > 0 ) {
 
-				link.classList.remove( 'filtered' );
+				link.classList.remove( 'hidden' );
 
 				for( var i = 0; i < res.length; i++ ) {
 					text = name.replace( res[ i ], '<b>' + res[ i ] + '</b>' );
@@ -423,7 +228,7 @@
 
 			} else {
 
-				link.classList.add( 'filtered' );
+				link.classList.add( 'hidden' );
 				link.innerHTML = name;
 
 			}
@@ -449,7 +254,7 @@
 
 					var file = section[ i ];
 
-					if( !links[ file ].classList.contains( 'filtered' ) ){
+					if ( links[ file ].classList.contains( 'hidden' ) === false ){
 
 						collapsed = false;
 						break;
@@ -460,13 +265,13 @@
 
 				var element = document.querySelector( 'h2[data-category="' + key + '"]' );
 
-				if( collapsed ){
+				if ( collapsed ) {
 
-					element.classList.add( 'filtered' );
+					element.classList.add( 'hidden' );
 
 				} else {
 
-					element.classList.remove( 'filtered' );
+					element.classList.remove( 'hidden' );
 
 				}
 

+ 102 - 0
examples/js/QuickHull.js

@@ -10,6 +10,8 @@
 	var Visible = 0;
 	var Deleted = 1;
 
+	var v1 = new THREE.Vector3();
+
 	function QuickHull() {
 
 		this.tolerance = - 1;
@@ -119,6 +121,106 @@
 
 		},
 
+		containsPoint: function ( point ) {
+
+			var faces = this.faces;
+
+			for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+				var face = faces[ i ];
+
+				// compute signed distance and check on what half space the point lies
+
+				if ( face.distanceToPoint( point ) > this.tolerance ) return false;
+
+			}
+
+			return true;
+
+		},
+
+		intersectRay: function ( ray, target ) {
+
+			// based on "Fast Ray-Convex Polyhedron Intersection"  by Eric Haines, GRAPHICS GEMS II
+
+			var faces = this.faces;
+
+			var tNear = - Infinity;
+			var tFar = Infinity;
+
+			for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+				var face = faces[ i ];
+
+				// interpret faces as planes for the further computation
+
+				var vN = face.distanceToPoint( ray.origin );
+				var vD = face.normal.dot( ray.direction );
+
+				// if the origin is on the positive side of a plane (so the plane can "see" the origin) and
+				// the ray is turned away or parallel to the plane, there is no intersection
+
+				if ( vN > 0 && vD >= 0 ) return null;
+
+				// compute the distance from the ray’s origin to the intersection with the plane
+
+				var t = ( vD !== 0 ) ? ( - vN / vD ) : 0;
+
+				// only proceed if the distance is positive. a negative distance means the intersection point
+				// lies "behind" the origin
+
+				if ( t <= 0 ) continue;
+
+				// now categorized plane as front-facing or back-facing
+
+				if ( vD > 0 ) {
+
+					//  plane faces away from the ray, so this plane is a back-face
+
+					tFar = Math.min( t, tFar );
+
+				} else {
+
+					// front-face
+
+					tNear = Math.max( t, tNear );
+
+				}
+
+				if ( tNear > tFar ) {
+
+					// if tNear ever is greater than tFar, the ray must miss the convex hull
+
+					return null;
+
+				}
+
+			}
+
+			// evaluate intersection point
+
+			// always try tNear first since its the closer intersection point
+
+			if ( tNear !== - Infinity ) {
+
+				ray.at( tNear, target );
+
+			} else {
+
+				ray.at( tFar, target );
+
+			}
+
+			return target;
+
+		},
+
+		intersectsRay: function ( ray ) {
+
+			return this.intersectRay( ray, v1 ) !== null;
+
+		},
+
 		makeEmpty: function () {
 
 			this.faces = [];

+ 1 - 1
examples/js/controls/DragControls.js

@@ -21,7 +21,7 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 	var _intersection = new THREE.Vector3();
 	var _worldPosition = new THREE.Vector3();
 	var _inverseMatrix = new THREE.Matrix4();
-	
+
 	var _selected = null, _hovered = null;
 
 	//

+ 23 - 23
examples/js/controls/PointerLockControls.js

@@ -5,19 +5,20 @@
 
 THREE.PointerLockControls = function ( camera, domElement ) {
 
-	var scope = this;
-
 	this.domElement = domElement || document.body;
 	this.isLocked = false;
 
-	camera.rotation.set( 0, 0, 0 );
+	//
+	// internals
+	//
 
-	var pitchObject = new THREE.Object3D();
-	pitchObject.add( camera );
+	var scope = this;
 
-	var yawObject = new THREE.Object3D();
-	yawObject.position.y = 10;
-	yawObject.add( pitchObject );
+	var changeEvent = { type: 'change' };
+	var lockEvent = { type: 'lock' };
+	var unlockEvent = { type: 'unlock' };
+
+	var euler = new THREE.Euler( 0, 0, 0, 'YXZ' );
 
 	var PI_2 = Math.PI / 2;
 
@@ -28,10 +29,16 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
 		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
 
-		yawObject.rotation.y -= movementX * 0.002;
-		pitchObject.rotation.x -= movementY * 0.002;
+		euler.setFromQuaternion( camera.quaternion );
+
+		euler.y -= movementX * 0.002;
+		euler.x -= movementY * 0.002;
 
-		pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
+		euler.x = Math.max( - PI_2, Math.min( PI_2, euler.x ) );
+
+		camera.quaternion.setFromEuler( euler );
+
+		scope.dispatchEvent( changeEvent );
 
 	}
 
@@ -39,13 +46,13 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 
 		if ( document.pointerLockElement === scope.domElement ) {
 
-			scope.dispatchEvent( { type: 'lock' } );
+			scope.dispatchEvent( lockEvent );
 
 			scope.isLocked = true;
 
 		} else {
 
-			scope.dispatchEvent( { type: 'unlock' } );
+			scope.dispatchEvent( unlockEvent );
 
 			scope.isLocked = false;
 
@@ -81,26 +88,19 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 
 	};
 
-	this.getObject = function () {
+	this.getObject = function () { // retaining this method for backward compatibility
 
-		return yawObject;
+		return camera;
 
 	};
 
 	this.getDirection = function () {
 
-		// assumes the camera itself is not rotated
-
 		var direction = new THREE.Vector3( 0, 0, - 1 );
-		var rotation = new THREE.Euler( 0, 0, 0, 'YXZ' );
 
 		return function ( v ) {
 
-			rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 );
-
-			v.copy( direction ).applyEuler( rotation );
-
-			return v;
+			return v.copy( direction ).applyQuaternion( camera.quaternion );
 
 		};
 

+ 1 - 1
examples/js/controls/TrackballControls.js

@@ -492,7 +492,7 @@ THREE.TrackballControls = function ( object, domElement ) {
 	function touchstart( event ) {
 
 		if ( _this.enabled === false ) return;
-		
+
 		event.preventDefault();
 
 		switch ( event.touches.length ) {

+ 1 - 0
examples/js/controls/TransformControls.js

@@ -988,6 +988,7 @@ THREE.TransformControlsGizmo = function () {
 				var tempGeometry = object.geometry.clone();
 				tempGeometry.applyMatrix(object.matrix);
 				object.geometry = tempGeometry;
+				object.renderOrder = Infinity;
 
 				object.position.set( 0, 0, 0 );
 				object.rotation.set( 0, 0, 0 );

+ 6 - 6
examples/js/effects/AnaglyphEffect.js

@@ -12,9 +12,9 @@ THREE.AnaglyphEffect = function ( renderer, width, height ) {
 
 	this.colorMatrixLeft = new THREE.Matrix3().fromArray( [
 
-			1.0671679973602295, 	-0.0016435992438346148,		 0.0001777536963345483, // r out
-			-0.028107794001698494,	-0.00019593400065787137,	-0.0002875397040043026, // g out
-			-0.04279090091586113,	 0.000015809757314855233,	-0.00024287120322696865 // b out
+		1.0671679973602295, - 0.0016435992438346148, 0.0001777536963345483, // r out
+		- 0.028107794001698494, - 0.00019593400065787137, - 0.0002875397040043026, // g out
+		- 0.04279090091586113, 0.000015809757314855233, - 0.00024287120322696865 // b out
 
 	] );
 
@@ -22,9 +22,9 @@ THREE.AnaglyphEffect = function ( renderer, width, height ) {
 
 	this.colorMatrixRight = new THREE.Matrix3().fromArray( [
 
-			-0.0355340838432312,	-0.06440307199954987,		 0.018319187685847282,	// r out
-			-0.10269022732973099,	 0.8079727292060852,		-0.04835830628871918,	// g out
-			0.0001224992738571018,	-0.009558862075209618,		 0.567823588848114		// b out
+		- 0.0355340838432312, - 0.06440307199954987, 0.018319187685847282, // r out
+		- 0.10269022732973099, 0.8079727292060852, - 0.04835830628871918, // g out
+		0.0001224992738571018, - 0.009558862075209618, 0.567823588848114 // b out
 
 	] );
 

+ 46 - 4
examples/js/effects/OutlineEffect.js

@@ -3,6 +3,41 @@
  *
  * Reference: https://en.wikipedia.org/wiki/Cel_shading
  *
+ * API
+ *
+ * 1. Traditional
+ *
+ * var effect = new THREE.OutlineEffect( renderer );
+ *
+ * function render() {
+ *
+ * 	effect.render( scene, camera );
+ *
+ * }
+ *
+ * 2. VR compatible
+ *
+ * var effect = new THREE.OutlineEffect( renderer );
+ * var renderingOutline = false;
+ *
+ * scene.onAfterRender = function () {
+ *
+ * 	if ( renderingOutline ) return;
+ *
+ * 	renderingOutline = true;
+ *
+ * 	effect.renderOutline( scene, camera );
+ *
+ * 	renderingOutline = false;
+ *
+ * };
+ *
+ * function render() {
+ *
+ * 	renderer.render( scene, camera );
+ *
+ * }
+ *
  * // How to set default outline parameters
  * new THREE.OutlineEffect( renderer, {
  * 	defaultThickness: 0.01,
@@ -404,7 +439,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	this.render = function ( scene, camera ) {
 
-		var renderTarget = null;
+		var renderTarget;
 		var forceClear = false;
 
 		if ( arguments[ 2 ] !== undefined ) {
@@ -421,7 +456,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 		}
 
-		renderer.setRenderTarget( renderTarget );
+		if ( renderTarget !== undefined ) renderer.setRenderTarget( renderTarget );
 
 		if ( forceClear ) renderer.clear();
 
@@ -435,10 +470,17 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 		var currentAutoClear = renderer.autoClear;
 		renderer.autoClear = this.autoClear;
 
-		// 1. render normally
 		renderer.render( scene, camera );
 
-		// 2. render outline
+		renderer.autoClear = currentAutoClear;
+
+		this.renderOutline( scene, camera );
+
+	};
+
+	this.renderOutline = function ( scene, camera ) {
+
+		var currentAutoClear = renderer.autoClear;
 		var currentSceneAutoUpdate = scene.autoUpdate;
 		var currentSceneBackground = scene.background;
 		var currentShadowMapEnabled = renderer.shadowMap.enabled;

+ 32 - 10
examples/js/exporters/GLTFExporter.js

@@ -123,9 +123,23 @@ THREE.GLTFExporter.prototype = {
 
 		var cachedCanvas;
 
+		var uids = new Map();
+		var uid = 0;
+
 		/**
-		 * Compare two arrays
+		 * Assign and return a temporal unique id for an object
+		 * especially which doesn't have .uuid
+		 * @param  {Object} object
+		 * @return {Integer}
 		 */
+		function getUID( object ) {
+
+			if ( ! uids.has( object ) ) uids.set( object, uid ++ );
+
+			return uids.get( object );
+
+		}
+
 		/**
 		 * Compare two arrays
 		 * @param  {Array} array1 Array 1 to compare
@@ -1178,9 +1192,9 @@ THREE.GLTFExporter.prototype = {
 
 				}
 
-				if ( cachedData.attributes.has( attribute ) ) {
+				if ( cachedData.attributes.has( getUID( attribute ) ) ) {
 
-					attributes[ attributeName ] = cachedData.attributes.get( attribute );
+					attributes[ attributeName ] = cachedData.attributes.get( getUID( attribute ) );
 					continue;
 
 				}
@@ -1201,7 +1215,7 @@ THREE.GLTFExporter.prototype = {
 				if ( accessor !== null ) {
 
 					attributes[ attributeName ] = accessor;
-					cachedData.attributes.set( attribute, accessor );
+					cachedData.attributes.set( getUID( attribute ), accessor );
 
 				}
 
@@ -1267,9 +1281,9 @@ THREE.GLTFExporter.prototype = {
 
 						var baseAttribute = geometry.attributes[ attributeName ];
 
-						if ( cachedData.attributes.has( attribute ) ) {
+						if ( cachedData.attributes.has( getUID( attribute ) ) ) {
 
-							target[ gltfAttributeName ] = cachedData.attributes.get( attribute );
+							target[ gltfAttributeName ] = cachedData.attributes.get( getUID( attribute ) );
 							continue;
 
 						}
@@ -1289,7 +1303,7 @@ THREE.GLTFExporter.prototype = {
 						}
 
 						target[ gltfAttributeName ] = processAccessor( relativeAttribute, geometry );
-						cachedData.attributes.set( baseAttribute, target[ gltfAttributeName ] );
+						cachedData.attributes.set( getUID( baseAttribute ), target[ gltfAttributeName ] );
 
 					}
 
@@ -1358,14 +1372,22 @@ THREE.GLTFExporter.prototype = {
 
 				if ( geometry.index !== null ) {
 
-					if ( cachedData.attributes.has( geometry.index ) ) {
+					var cacheKey = getUID( geometry.index );
+
+					if ( groups[ i ].start !== undefined || groups[ i ].count !== undefined ) {
+
+						cacheKey += ':' + groups[ i ].start + ':' + groups[ i ].count;
+
+					}
+
+					if ( cachedData.attributes.has( cacheKey ) ) {
 
-						primitive.indices = cachedData.attributes.get( geometry.index );
+						primitive.indices = cachedData.attributes.get( cacheKey );
 
 					} else {
 
 						primitive.indices = processAccessor( geometry.index, geometry, groups[ i ].start, groups[ i ].count );
-						cachedData.attributes.set( geometry.index, primitive.indices );
+						cachedData.attributes.set( cacheKey, primitive.indices );
 
 					}
 

+ 163 - 159
examples/js/geometries/LightningStrike.js

@@ -2,85 +2,85 @@
  * @author yomboprime https://github.com/yomboprime
  *
  * @fileoverview LightningStrike object for creating lightning strikes and voltaic arcs.
- * 
- * 
+ *
+ *
  * Usage
- * 
+ *
  * var myRay = new THREE.LightningStrike( paramsObject );
  * var myRayMesh = new THREE.Mesh( myRay, myMaterial );
  * scene.add( myRayMesh );
  * ...
  * myRay.update( currentTime );
- * 
+ *
  * The "currentTime" can vary its rate, go forwards, backwards or even jump, but it cannot be negative.
- * 
+ *
  * You should normally leave the ray position to (0, 0, 0). You should control it by changing the sourceOffset and destOffset parameters.
- * 
- * 
+ *
+ *
  * LightningStrike parameters
- * 
+ *
  * The paramsObject can contain any of the following parameters.
- * 
+ *
  * Legend:
  * 'LightningStrike' (also called 'ray'): An independent voltaic arc with its ramifications and defined with a set of parameters.
  * 'Subray': A ramification of the ray. It is not a LightningStrike object.
  * 'Segment': A linear segment piece of a subray.
  * 'Leaf segment': A ray segment which cannot be smaller.
- * 
- * 
+ *
+ *
  * The following parameters can be changed any time and if they vary smoothly, the ray form will also change smoothly:
- * 
+ *
  * @param {Vector3} sourceOffset The point where the ray starts.
- * 
+ *
  * @param {Vector3} destOffset The point where the ray ends.
- * 
+ *
  * @param {double} timeScale The rate at wich the ray form changes in time. Default: 1
- * 
+ *
  * @param {double} roughness From 0 to 1. The higher the value, the more wrinkled is the ray. Default: 0.9
- * 
+ *
  * @param {double} straightness From 0 to 1. The higher the value, the more straight will be a subray path. Default: 0.7
- * 
+ *
  * @param {Vector3} up0 Ray 'up' direction at the ray starting point. Must be normalized. It should be perpendicular to the ray forward direction but it doesn't matter much.
- * 
+ *
  * @param {Vector3} up1 Like the up0 parameter but at the end of the ray. Must be normalized.
- * 
+ *
  * @param {double} radius0 Radius of the main ray trunk at the start point. Default: 1
- * 
+ *
  * @param {double} radius1 Radius of the main ray trunk at the end point. Default: 1
- * 
+ *
  * @param {double} radius0Factor The radius0 of a subray is this factor times the radius0 of its parent subray. Default: 0.5
- * 
+ *
  * @param {double} radius1Factor The radius1 of a subray is this factor times the radius1 of its parent subray. Default: 0.2
- * 
+ *
  * @param {minRadius} Minimum value a subray radius0 or radius1 can get. Default: 0.1
- * 
- * 
+ *
+ *
  * The following parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly:
- * 
+ *
  * @param {boolean} isEternal If true the ray never extinguishes. Otherwise its life is controlled by the 'birthTime' and 'deathTime' parameters. Default: true if any of those two parameters is undefined.
- * 
+ *
  * @param {double} birthTime The time at which the ray starts its life and begins propagating. Only if isEternal is false. Default: None.
- * 
+ *
  * @param {double} deathTime The time at which the ray ends vanishing and its life. Only if isEternal is false. Default: None.
- * 
+ *
  * @param {double} propagationTimeFactor From 0 to 1. Lifetime factor at which the ray ends propagating and enters the steady phase. For example, 0.1 means it is propagating 1/10 of its lifetime. Default: 0.1
- * 
+ *
  * @param {double} vanishingTimeFactor From 0 to 1. Lifetime factor at which the ray ends the steady phase and begins vanishing. For example, 0.9 means it is vanishing 1/10 of its lifetime. Default: 0.9
- * 
+ *
  * @param {double} subrayPeriod Subrays cycle periodically. This is their time period. Default: 4
- * 
+ *
  * @param {double} subrayDutyCycle From 0 to 1. This is the fraction of time a subray is active. Default: 0.6
- * 
- * 
+ *
+ *
  * These parameters cannot change after lightning creation:
- * 
+ *
  * @param {integer} maxIterations: Greater than 0. The number of ray's leaf segments is 2**maxIterations. Default: 9
- * 
+ *
  * @param {boolean} isStatic Set to true only for rays which won't change over time and are not attached to moving objects (Rare case). It is used to set the vertex buffers non-dynamic. You can omit calling update() for these rays.
- *  
+ *
  * @param {integer} ramification Greater than 0. Maximum number of child subrays a subray can have. Default: 5
  *
- * @param {integer} maxSubrayRecursion Greater than 0. Maximum level of recursion (subray descendant generations). Default: 3 
+ * @param {integer} maxSubrayRecursion Greater than 0. Maximum level of recursion (subray descendant generations). Default: 3
  *
  * @param {double} recursionProbability From 0 to 1. The lower the value, the less chance each new generation of subrays has to generate new subrays. Default: 0.6
  *
@@ -91,13 +91,13 @@
  * The randomGenerator parameter should be an object with a random() function similar to Math.random, but seedable.
  * It must have also a getSeed() method, which returns the current seed, and a setSeed( seed ) method, which accepts as seed a fractional number from 0 to 1, as well as any other number.
  * The default value is an internal generator for some uses and Math.random for others (It is non-repeatable even if noiseSeed is supplied)
- * 
+ *
  * @param {double} noiseSeed Seed used to make repeatable rays (see the randomGenerator)
- * 
+ *
  * @param {function} onDecideSubrayCreation Set this to change the callback which decides subray creation. You can look at the default callback in the code (createDefaultSubrayCreationCallbacks)for more info.
- * 
+ *
  * @param {function} onSubrayCreation This is another callback, more simple than the previous one. It can be used to adapt the form of subrays or other parameters once a subray has been created and initialized. It is used in the examples to adapt subrays to a sphere or to a plane.
- * 
+ *
  *
 */
 
@@ -138,7 +138,7 @@ THREE.LightningStrike.createRandomGenerator = function () {
 	var numSeeds = 2053;
 	var seeds = [];
 
-	for ( var i = 0; i < numSeeds; i++ ) {
+	for ( var i = 0; i < numSeeds; i ++ ) {
 
 		seeds.push( Math.random() );
 
@@ -176,25 +176,24 @@ THREE.LightningStrike.createRandomGenerator = function () {
 
 };
 
-THREE.LightningStrike.copyParameters = function ( dest, source) {
+THREE.LightningStrike.copyParameters = function ( dest, source ) {
 
 	source = source || {};
 	dest = dest || {};
 
-	var vecCopy = function( v ) {
+	var vecCopy = function ( v ) {
 
 		if ( source === dest ) {
 
 			return v;
 
-		}
-		else {
+		} else {
 
 			return v.clone();
 
 		}
 
-	}
+	};
 
 	dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy( source.sourceOffset ) : new THREE.Vector3( 0, 100, 0 ),
 	dest.destOffset = source.destOffset !== undefined ? vecCopy( source.destOffset ) : new THREE.Vector3( 0, 0, 0 ),
@@ -223,7 +222,7 @@ THREE.LightningStrike.copyParameters = function ( dest, source) {
 
 	// These parameters cannot change after lightning creation:
 
-	dest.maxIterations =  source.maxIterations !== undefined ? source.maxIterations : 9;
+	dest.maxIterations = source.maxIterations !== undefined ? source.maxIterations : 9;
 	dest.isStatic = source.isStatic !== undefined ? source.isStatic : false;
 	dest.ramification = source.ramification !== undefined ? source.ramification : 5;
 	dest.maxSubrayRecursion = source.maxSubrayRecursion !== undefined ? source.maxSubrayRecursion : 3;
@@ -240,25 +239,21 @@ THREE.LightningStrike.copyParameters = function ( dest, source) {
 
 THREE.LightningStrike.prototype.update = function ( time ) {
 
-	if ( this.isStatic ) {
-		return;
-	}
-	
+	if ( this.isStatic ) return;
+
 	if ( this.rayParameters.isEternal || ( this.rayParameters.birthTime <= time && time <= this.rayParameters.deathTime ) ) {
 
 		this.updateMesh( time );
 
 		if ( time < this.subrays[ 0 ].endPropagationTime ) {
-		
+
 			this.state = THREE.LightningStrike.RAY_PROPAGATING;
 
-		}	
-		else if ( time > this.subrays[ 0 ].beginVanishingTime ) {
+		} else if ( time > this.subrays[ 0 ].beginVanishingTime ) {
 
 			this.state = THREE.LightningStrike.RAY_VANISHING;
 
-		}
-		else {
+		} else {
 
 			this.state = THREE.LightningStrike.RAY_STEADY;
 
@@ -266,8 +261,7 @@ THREE.LightningStrike.prototype.update = function ( time ) {
 
 		this.visible = true;
 
-	}
-	else {
+	} else {
 
 		this.visible = false;
 
@@ -275,8 +269,7 @@ THREE.LightningStrike.prototype.update = function ( time ) {
 
 			this.state = THREE.LightningStrike.RAY_UNBORN;
 
-		}
-		else {
+		} else {
 
 			this.state = THREE.LightningStrike.RAY_EXTINGUISHED;
 
@@ -294,7 +287,7 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 
 	// These parameters cannot change after lightning creation:
 
-	this.maxIterations =  rayParameters.maxIterations !== undefined ? Math.floor( rayParameters.maxIterations ) : 9;
+	this.maxIterations = rayParameters.maxIterations !== undefined ? Math.floor( rayParameters.maxIterations ) : 9;
 	rayParameters.maxIterations = this.maxIterations;
 	this.isStatic = rayParameters.isStatic !== undefined ? rayParameters.isStatic : false;
 	rayParameters.isStatic = this.isStatic;
@@ -314,13 +307,12 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 		this.seedGenerator = rayParameters.randomGenerator;
 
 		if ( rayParameters.noiseSeed !== undefined ) {
-		
+
 			this.seedGenerator.setSeed( rayParameters.noiseSeed );
 
 		}
 
-	}
-	else {
+	} else {
 
 		this.randomGenerator = THREE.LightningStrike.createRandomGenerator();
 		this.seedGenerator = Math;
@@ -332,8 +324,7 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 
 		this.onDecideSubrayCreation = rayParameters.onDecideSubrayCreation;
 
-	}
-	else {
+	} else {
 
 		this.createDefaultSubrayCreationCallbacks();
 
@@ -351,12 +342,12 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 
 	this.maxSubrays = Math.ceil( 1 + Math.pow( this.ramification, Math.max( 0, this.maxSubrayRecursion - 1 ) ) );
 	rayParameters.maxSubrays = this.maxSubrays;
-	
+
 	this.maxRaySegments = 2 * ( 1 << this.maxIterations );
 
 	this.subrays = [];
 
-	for ( var i = 0; i < this.maxSubrays; i++ ) {
+	for ( var i = 0; i < this.maxSubrays; i ++ ) {
 
 		this.subrays.push( this.createSubray() );
 
@@ -364,7 +355,7 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 
 	this.raySegments = [];
 
-	for ( var i = 0; i < this.maxRaySegments; i++ ) {
+	for ( var i = 0; i < this.maxRaySegments; i ++ ) {
 
 		this.raySegments.push( this.createSegment() );
 
@@ -389,7 +380,7 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 	this.indices = null;
 	this.positionAttribute = null;
 	this.uvsAttribute = null;
-	
+
 	this.simplexX = new SimplexNoise( this.seedGenerator );
 	this.simplexY = new SimplexNoise( this.seedGenerator );
 	this.simplexZ = new SimplexNoise( this.seedGenerator );
@@ -410,46 +401,56 @@ THREE.LightningStrike.prototype.init = function ( rayParameters ) {
 THREE.LightningStrike.prototype.createMesh = function () {
 
 	var maxDrawableSegmentsPerSubRay = 1 << this.maxIterations;
-	
+
 	var maxVerts = 3 * ( maxDrawableSegmentsPerSubRay + 1 ) * this.maxSubrays;
 	var maxIndices = 18 * maxDrawableSegmentsPerSubRay * this.maxSubrays;
 
 	this.vertices = new Float32Array( maxVerts * 3 );
 	this.indices = new Uint32Array( maxIndices );
 	if ( this.generateUVs ) {
+
 		this.uvs = new Float32Array( maxVerts * 2 );
+
 	}
 
 	// Populate the mesh
 	this.fillMesh( 0 );
-	
+
 	this.setIndex( new THREE.Uint32BufferAttribute( this.indices, 1 ) );
 
 	this.positionAttribute = new THREE.Float32BufferAttribute( this.vertices, 3 );
 	this.addAttribute( 'position', this.positionAttribute );
 
-	if ( this.generateUVs ) {1
+	if ( this.generateUVs ) {
+
 		this.uvsAttribute = new THREE.Float32BufferAttribute( new Float32Array( this.uvs ), 2 );
 		this.addAttribute( 'uv', this.uvsAttribute );
+
 	}
 
 	if ( ! this.isStatic ) {
+
 		this.index.dynamic = true;
 		this.positionAttribute.dynamic = true;
 		if ( this.generateUVs ) {
+
 			this.uvsAttribute.dynamic = true;
+
 		}
+
 	}
 
 	// Store buffers for later modification
 	this.vertices = this.positionAttribute.array;
 	this.indices = this.index.array;
 	if ( this.generateUVs ) {
+
 		this.uvs = this.uvsAttribute.array;
+
 	}
 
 };
-	
+
 THREE.LightningStrike.prototype.updateMesh = function ( time ) {
 
 	this.fillMesh( time );
@@ -461,7 +462,9 @@ THREE.LightningStrike.prototype.updateMesh = function ( time ) {
 	this.positionAttribute.needsUpdate = true;
 
 	if ( this.generateUVs ) {
+
 		this.uvsAttribute.needsUpdate = true;
+
 	}
 
 };
@@ -475,16 +478,15 @@ THREE.LightningStrike.prototype.fillMesh = function ( time ) {
 	this.currentCoordinate = 0;
 	this.currentUVCoordinate = 0;
 
-	this.fractalRay( time, function fillVertices ( segment ) {
+	this.fractalRay( time, function fillVertices( segment ) {
 
 		var subray = scope.currentSubray;
 
-		if ( time < subray.birthTime ) {//&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) {
+		if ( time < subray.birthTime ) { //&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) {
 
 			return;
 
-		}
-		else if ( this.rayParameters.isEternal && scope.currentSubray.recursion == 0 ) {
+		} else if ( this.rayParameters.isEternal && scope.currentSubray.recursion == 0 ) {
 
 			// Eternal rays don't propagate nor vanish, but its subrays do
 
@@ -492,8 +494,7 @@ THREE.LightningStrike.prototype.fillMesh = function ( time ) {
 
 			scope.onDecideSubrayCreation( segment, scope );
 
-		}
-		else if ( time < subray.endPropagationTime ) {
+		} else if ( time < subray.endPropagationTime ) {
 
 			if ( scope.timeFraction >= segment.fraction0 * subray.propagationTimeFactor ) {
 
@@ -505,8 +506,7 @@ THREE.LightningStrike.prototype.fillMesh = function ( time ) {
 
 			}
 
-		}
-		else if ( time < subray.beginVanishingTime ) {
+		} else if ( time < subray.beginVanishingTime ) {
 
 			// Ray is steady (nor propagating nor vanishing)
 
@@ -514,10 +514,9 @@ THREE.LightningStrike.prototype.fillMesh = function ( time ) {
 
 			scope.onDecideSubrayCreation( segment, scope );
 
-		}
-		else {
+		} else {
 
-			if ( scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1  * ( 1 - subray.vanishingTimeFactor ) ) {
+			if ( scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1 * ( 1 - subray.vanishingTimeFactor ) ) {
 
 				// Segment has not yet vanished
 
@@ -535,7 +534,7 @@ THREE.LightningStrike.prototype.fillMesh = function ( time ) {
 
 THREE.LightningStrike.prototype.addNewSubray = function ( rayParameters ) {
 
-	return this.subrays[ this.numSubrays++ ];
+	return this.subrays[ this.numSubrays ++ ];
 
 };
 
@@ -571,7 +570,7 @@ THREE.LightningStrike.prototype.fractalRay = function ( time, segmentCallback )
 	this.initSubray( this.addNewSubray(), this.rayParameters );
 
 	// Process all subrays that are being generated until consuming all of them
-	for ( var subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex++ ) {
+	for ( var subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex ++ ) {
 
 		var subray = this.subrays[ subrayIndex ];
 		this.currentSubray = subray;
@@ -587,7 +586,7 @@ THREE.LightningStrike.prototype.fractalRay = function ( time, segmentCallback )
 
 		this.timeFraction = ( time - subray.birthTime ) / ( subray.deathTime - subray.birthTime );
 
-		this.currentSegmentIndex  = 0;
+		this.currentSegmentIndex = 0;
 		this.isInitialSegment = true;
 
 		var segment = this.getNewSegment();
@@ -630,9 +629,11 @@ THREE.LightningStrike.prototype.fractalRayRecursive = function ( segment ) {
 	this.forwards.subVectors( segment.pos1, segment.pos0 );
 	var lForwards = this.forwards.length();
 
-	if ( lForwards < 0.000001) {
+	if ( lForwards < 0.000001 ) {
+
 		this.forwards.set( 0, 0, 0.01 );
 		lForwards = this.forwards.length();
+
 	}
 
 	var middleRadius = ( segment.radius0 + segment.radius1 ) * 0.5;
@@ -644,10 +645,10 @@ THREE.LightningStrike.prototype.fractalRayRecursive = function ( segment ) {
 	this.middleLinPos.lerpVectors( segment.linPos0, segment.linPos1, 0.5 );
 	var p = this.middleLinPos;
 
-	// Noise	
+	// Noise
 	this.newPos.set( this.simplexX.noise4d( p.x, p.y, p.z, timeDimension ),
-						this.simplexY.noise4d( p.x, p.y, p.z, timeDimension ),
-						this.simplexZ.noise4d( p.x, p.y, p.z, timeDimension ) );
+		this.simplexY.noise4d( p.x, p.y, p.z, timeDimension ),
+		this.simplexZ.noise4d( p.x, p.y, p.z, timeDimension ) );
 
 	this.newPos.multiplyScalar( segment.positionVariationFactor * lForwards );
 	this.newPos.add( this.middlePos );
@@ -698,7 +699,7 @@ THREE.LightningStrike.prototype.createPrism = function ( segment ) {
 	if ( this.isInitialSegment ) {
 
 		this.currentCreateTriangleVertices( segment.pos0, segment.up0, this.forwardsFill, segment.radius0, 0 );
-		
+
 		this.isInitialSegment = false;
 
 	}
@@ -712,7 +713,7 @@ THREE.LightningStrike.prototype.createPrism = function ( segment ) {
 THREE.LightningStrike.prototype.createTriangleVerticesWithoutUVs = function ( pos, up, forwards, radius ) {
 
 	// Create an equilateral triangle (only vertices)
-	
+
 	this.side.crossVectors( up, forwards ).multiplyScalar( radius * THREE.LightningStrike.COS30DEG );
 	this.down.copy( up ).multiplyScalar( - radius * THREE.LightningStrike.SIN30DEG );
 
@@ -721,21 +722,21 @@ THREE.LightningStrike.prototype.createTriangleVerticesWithoutUVs = function ( po
 
 	p.copy( pos ).sub( this.side ).add( this.down );
 
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
 
 	p.copy( pos ).add( this.side ).add( this.down );
 
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
 
 	p.copy( up ).multiplyScalar( radius ).add( pos );
-	
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
 
 	this.currentVertex += 3;
 
@@ -744,7 +745,7 @@ THREE.LightningStrike.prototype.createTriangleVerticesWithoutUVs = function ( po
 THREE.LightningStrike.prototype.createTriangleVerticesWithUVs = function ( pos, up, forwards, radius, u ) {
 
 	// Create an equilateral triangle (only vertices)
-	
+
 	this.side.crossVectors( up, forwards ).multiplyScalar( radius * THREE.LightningStrike.COS30DEG );
 	this.down.copy( up ).multiplyScalar( - radius * THREE.LightningStrike.SIN30DEG );
 
@@ -754,30 +755,30 @@ THREE.LightningStrike.prototype.createTriangleVerticesWithUVs = function ( pos,
 
 	p.copy( pos ).sub( this.side ).add( this.down );
 
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
 
-	uv[ this.currentUVCoordinate++ ] = u;
-	uv[ this.currentUVCoordinate++ ] = 0;
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 0;
 
 	p.copy( pos ).add( this.side ).add( this.down );
 
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
 
-	uv[ this.currentUVCoordinate++ ] = u;
-	uv[ this.currentUVCoordinate++ ] = 0.5;
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 0.5;
 
 	p.copy( up ).multiplyScalar( radius ).add( pos );
-	
-	v[ this.currentCoordinate++ ] = p.x;
-	v[ this.currentCoordinate++ ] = p.y;
-	v[ this.currentCoordinate++ ] = p.z;
 
-	uv[ this.currentUVCoordinate++ ] = u;
-	uv[ this.currentUVCoordinate++ ] = 1;
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 1;
 
 	this.currentVertex += 3;
 
@@ -788,24 +789,24 @@ THREE.LightningStrike.prototype.createPrismFaces = function ( vertex, index ) {
 	var indices = this.indices;
 	var vertex = this.currentVertex - 6;
 
-	indices[ this.currentIndex++ ] = vertex + 1;
-	indices[ this.currentIndex++ ] = vertex + 2;
-	indices[ this.currentIndex++ ] = vertex + 5;
-	indices[ this.currentIndex++ ] = vertex + 1;
-	indices[ this.currentIndex++ ] = vertex + 5;
-	indices[ this.currentIndex++ ] = vertex + 4;
-	indices[ this.currentIndex++ ] = vertex + 0;
-	indices[ this.currentIndex++ ] = vertex + 1;
-	indices[ this.currentIndex++ ] = vertex + 4;
-	indices[ this.currentIndex++ ] = vertex + 0;
-	indices[ this.currentIndex++ ] = vertex + 4;
-	indices[ this.currentIndex++ ] = vertex + 3;
-	indices[ this.currentIndex++ ] = vertex + 2;
-	indices[ this.currentIndex++ ] = vertex + 0;
-	indices[ this.currentIndex++ ] = vertex + 3;
-	indices[ this.currentIndex++ ] = vertex + 2;
-	indices[ this.currentIndex++ ] = vertex + 3;
-	indices[ this.currentIndex++ ] = vertex + 5;
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 5;
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 5;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 5;
 
 };
 
@@ -821,9 +822,9 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 
 		var period = lightningStrike.rayParameters.subrayPeriod;
 		var dutyCycle = lightningStrike.rayParameters.subrayDutyCycle;
-		
+
 		var phase0 = ( lightningStrike.rayParameters.isEternal && subray.recursion == 0 ) ? - random1() * period : THREE.Math.lerp( subray.birthTime, subray.endPropagationTime, segment.fraction0 ) - random1() * period;
-		
+
 		var phase = lightningStrike.time - phase0;
 		var currentCycle = Math.floor( phase / period );
 
@@ -833,9 +834,12 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 
 		probability = lightningStrike.subrayProbability;
 		var probability = 0;
+
 		if ( isActive ) {
+
 			probability = lightningStrike.subrayProbability;
 			// Distribution test: probability *= segment.fraction0 > 0.5 && segment.fraction0 < 0.9 ? 1 / 0.4 : 0;
+
 		}
 
 		if ( subray.recursion < lightningStrike.maxSubrayRecursion && lightningStrike.numSubrays < lightningStrike.maxSubrays && random1() < probability ) {
@@ -850,12 +854,12 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 			childSubray.maxIterations = Math.max( 1, subray.maxIterations - 1 );
 
 			childSubray.linPos0.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
-			childSubray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );;
+			childSubray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
 			childSubray.up0.copy( subray.up0 );
 			childSubray.up1.copy( subray.up1 );
 			childSubray.radius0 = segment.radius0 * lightningStrike.rayParameters.radius0Factor;
 			childSubray.radius1 = Math.min( lightningStrike.rayParameters.minRadius, segment.radius1 * lightningStrike.rayParameters.radius1Factor );
-			
+
 			childSubray.birthTime = phase0 + ( currentCycle ) * period;
 			childSubray.deathTime = childSubray.birthTime + period * dutyCycle;
 
@@ -884,7 +888,7 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 	var vec2Forward = new THREE.Vector3();
 	var vec3Side = new THREE.Vector3();
 	var vec4Up = new THREE.Vector3();
-	
+
 	this.onSubrayCreation = function ( segment, parentSubray, childSubray, lightningStrike ) {
 
 		// Decide childSubray origin and destination positions (pos0 and pos1) and possibly other properties of childSubray
@@ -895,9 +899,9 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 	};
 
 	this.subrayConePosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
-		
+
 		// Sets childSubray pos0 and pos1 in a cone
-		
+
 		childSubray.pos0.copy( segment.pos0 );
 
 		vec1Pos.subVectors( parentSubray.pos1, parentSubray.pos0 );
@@ -906,15 +910,15 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 		var length = vec1Pos.length();
 		vec3Side.crossVectors( parentSubray.up0, vec2Forward );
 		var angle = 2 * Math.PI * random1();
-		vec3Side.multiplyScalar( Math.cos ( angle ) );
-		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin ( angle ) );
+		vec3Side.multiplyScalar( Math.cos( angle ) );
+		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
 
 		childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
 
-	}
+	};
 
 	this.subrayCylinderPosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
-		
+
 		// Sets childSubray pos0 and pos1 in a cylinder
 
 		childSubray.pos0.copy( segment.pos0 );
@@ -925,12 +929,12 @@ THREE.LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function
 		var length = vec1Pos.length();
 		vec3Side.crossVectors( parentSubray.up0, vec2Forward );
 		var angle = 2 * Math.PI * random1();
-		vec3Side.multiplyScalar( Math.cos ( angle ) );
-		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin ( angle ) );
+		vec3Side.multiplyScalar( Math.cos( angle ) );
+		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
 
 		childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
 
-	}
+	};
 
 };
 
@@ -978,19 +982,19 @@ THREE.LightningStrike.prototype.createSegment = function () {
 		fraction0: 0,
 		fraction1: 0,
 		positionVariationFactor: 0
-	}
+	};
 
 };
 
 THREE.LightningStrike.prototype.getNewSegment = function () {
 
-	return this.raySegments[ this.currentSegmentIndex++ ];
+	return this.raySegments[ this.currentSegmentIndex ++ ];
 
 };
 
 THREE.LightningStrike.prototype.copy = function ( source ) {
-	
-	BufferGeometry.prototype.copy.call( this, source );
+
+	THREE.BufferGeometry.prototype.copy.call( this, source );
 
 	this.init( THREE.LightningStrike.copyParameters( {}, source.rayParameters ) );
 

+ 3 - 3
examples/js/interactive/SelectionHelper.js

@@ -12,9 +12,9 @@ THREE.SelectionHelper = ( function () {
 
 		this.renderer = renderer;
 
-		this.startPoint = { x: 0, y: 0 };
-		this.pointTopLeft = { x: 0, y: 0 };
-		this.pointBottomRight = { x: 0, y: 0 };
+		this.startPoint = new THREE.Vector2();
+		this.pointTopLeft = new THREE.Vector2();
+		this.pointBottomRight = new THREE.Vector2();
 
 		this.isDown = false;
 

+ 121 - 0
examples/js/lights/LightProbeGenerator.js

@@ -0,0 +1,121 @@
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+THREE.LightProbeGenerator = {
+
+	// https://www.ppsloan.org/publications/StupidSH36.pdf
+	fromCubeTexture: function ( cubeTexture ) {
+
+		var norm, lengthSq, weight, totalWeight = 0;
+
+		var coord = new THREE.Vector3();
+
+		var dir = new THREE.Vector3();
+
+		var color = new THREE.Color();
+
+		var shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+
+		var sh = new THREE.SphericalHarmonics3();
+		var shCoefficients = sh.coefficients;
+
+		for ( var faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
+
+			var image = cubeTexture.image[ faceIndex ];
+
+			var width = image.width;
+			var height = image.height;
+
+			var canvas = document.createElement( 'canvas' );
+
+			canvas.width = width;
+			canvas.height = height;
+
+			var context = canvas.getContext( '2d' );
+
+			context.drawImage( image, 0, 0, width, height );
+
+			var imageData = context.getImageData( 0, 0, width, height );
+
+			var data = imageData.data;
+
+			var imageWidth = imageData.width; // assumed to be square
+
+			var pixelSize = 2 / imageWidth;
+
+			for ( var i = 0, il = data.length; i < il; i += 4 ) { // RGBA assumed
+
+				// pixel color
+				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
+
+				// convert to linear color space
+				color.copySRGBToLinear( color );
+
+				// pixel coordinate on unit cube
+
+				var pixelIndex = i / 4;
+
+				var col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+
+				var row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
+
+				switch ( faceIndex ) {
+
+					case 0: coord.set( - 1, row, - col ); break;
+
+					case 1: coord.set( 1, row, col ); break;
+
+					case 2: coord.set( - col, 1, - row ); break;
+
+					case 3: coord.set( - col, - 1, row ); break;
+
+					case 4: coord.set( - col, row, 1 ); break;
+
+					case 5: coord.set( col, row, - 1 ); break;
+
+				}
+
+				// weight assigned to this pixel
+
+				lengthSq = coord.lengthSq();
+
+				weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
+
+				totalWeight += weight;
+
+				// direction vector to this pixel
+				dir.copy( coord ).normalize();
+
+				// evaluate SH basis functions in direction dir
+				THREE.SphericalHarmonics3.getBasisAt( dir, shBasis );
+
+				// accummuulate
+				for ( var j = 0; j < 9; j ++ ) {
+
+					shCoefficients[ j ].x += shBasis[ j ] * color.r * weight;
+					shCoefficients[ j ].y += shBasis[ j ] * color.g * weight;
+					shCoefficients[ j ].z += shBasis[ j ] * color.b * weight;
+
+				}
+
+			}
+
+		}
+
+		// normalize
+		norm = ( 4 * Math.PI ) / totalWeight;
+
+		for ( var j = 0; j < 9; j ++ ) {
+
+			shCoefficients[ j ].x *= norm;
+			shCoefficients[ j ].y *= norm;
+			shCoefficients[ j ].z *= norm;
+
+		}
+
+		return new THREE.LightProbe( sh );
+
+	}
+
+};

+ 48 - 76
examples/js/loaders/LDrawLoader.js

@@ -271,18 +271,6 @@ THREE.LDrawLoader = ( function () {
 
 			}, onProgress, onError );
 
-			function subobjectLoad( url, onLoad, onProgress, onError, subobject ) {
-
-				var fileLoader = new THREE.FileLoader( scope.manager );
-				fileLoader.setPath( scope.path );
-				fileLoader.load( url, function ( text ) {
-
-					processObject( text, onLoad, subobject );
-
-				}, onProgress, onError );
-
-			}
-
 			function processObject( text, onProcessed, subobject ) {
 
 				var parseScope = scope.newParseScopeLevel();
@@ -292,6 +280,12 @@ THREE.LDrawLoader = ( function () {
 
 				// Add to cache
 				var currentFileName = parentParseScope.currentFileName;
+				if ( currentFileName !== null ) {
+
+					currentFileName = parentParseScope.currentFileName.toLowerCase();
+
+				}
+
 				if ( scope.subobjectCache[ currentFileName ] === undefined ) {
 
 					scope.subobjectCache[ currentFileName ] = text;
@@ -308,37 +302,39 @@ THREE.LDrawLoader = ( function () {
 				parseScope.numSubobjects = parseScope.subobjects.length;
 				parseScope.subobjectIndex = 0;
 
-				if ( parseScope.numSubobjects > 0 ) {
+				var finishedCount = 0;
+				onSubobjectFinish();
 
-					// Load the first subobject
-					var subobjectGroup = loadSubobject( parseScope.subobjects[ 0 ], true );
+				return objGroup;
 
-					// Optimization for loading pack: If subobjects are obtained from cache, keep loading them iteratively rather than recursively
-					if ( subobjectGroup ) {
+				function onSubobjectFinish() {
 
-						while ( subobjectGroup && parseScope.subobjectIndex < parseScope.numSubobjects - 1 ) {
+					finishedCount ++;
 
-							subobjectGroup = loadSubobject( parseScope.subobjects[ ++ parseScope.subobjectIndex ], true );
+					if ( finishedCount === parseScope.subobjects.length + 1 ) {
 
-						}
-
-						if ( subobjectGroup ) {
+						finalizeObject();
 
-							finalizeObject();
+					} else {
 
-						}
+						// Once the previous subobject has finished we can start processing the next one in the list.
+						// The subobject processing shares scope in processing so it's important that they be loaded serially
+						// to avoid race conditions.
+						// Promise.resolve is used as an approach to asynchronously schedule a task _before_ this frame ends to
+						// avoid stack overflow exceptions when loading many subobjects from the cache. RequestAnimationFrame
+						// will work but causes the load to happen after the next frame which causes the load to take significantly longer.
+						var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
+						Promise.resolve().then( function () {
 
-					}
+							loadSubobject( subobject );
 
-				} else {
+						} );
+						parseScope.subobjectIndex ++;
 
-					// No subobjects, finish object
-					finalizeObject();
+					}
 
 				}
 
-				return objGroup;
-
 				function finalizeObject() {
 
 					if ( ! scope.separateObjects && ! parentParseScope.isFromParse ) {
@@ -368,7 +364,7 @@ THREE.LDrawLoader = ( function () {
 
 				}
 
-				function loadSubobject( subobject, sync ) {
+				function loadSubobject( subobject ) {
 
 					parseScope.mainColourCode = subobject.material.userData.code;
 					parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
@@ -382,16 +378,15 @@ THREE.LDrawLoader = ( function () {
 					}
 
 					// If subobject was cached previously, use the cached one
-					var cached = scope.subobjectCache[ subobject.originalFileName ];
+					var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
 					if ( cached ) {
 
-						var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded, subobject );
-						if ( sync ) {
+						processObject( cached, function ( subobjectGroup ) {
 
-							addSubobject( subobject, subobjectGroup );
-							return subobjectGroup;
+							onSubobjectLoaded( subobjectGroup, subobject );
+							onSubobjectFinish();
 
-						}
+						}, subobject );
 
 						return;
 
@@ -451,22 +446,6 @@ THREE.LDrawLoader = ( function () {
 							// All location possibilities have been tried, give up loading this object
 							console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
 
-							// Try to read the next subobject
-							parseScope.subobjectIndex ++;
-
-							if ( parseScope.subobjectIndex >= parseScope.numSubobjects ) {
-
-								// All subojects have been loaded. Finish parent object
-								scope.removeScopeLevel();
-								onProcessed( objGroup );
-
-							} else {
-
-								// Load next subobject
-								loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
-
-							}
-
 							return;
 
 					}
@@ -481,15 +460,22 @@ THREE.LDrawLoader = ( function () {
 					fileLoader.setPath( scope.path );
 					fileLoader.load( subobjectURL, function ( text ) {
 
-						processObject( text, onSubobjectLoaded, subobject );
+						processObject( text, function ( subobjectGroup ) {
 
-					}, undefined, onSubobjectError );
+							onSubobjectLoaded( subobjectGroup, subobject );
+							onSubobjectFinish();
 
-				}
+						}, subobject );
+
+					}, undefined, function ( err ) {
+
+						onSubobjectError( err, subobject );
+
+					}, subobject );
 
-				function onSubobjectLoaded( subobjectGroup ) {
+				}
 
-					var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
+				function onSubobjectLoaded( subobjectGroup, subobject ) {
 
 					if ( subobjectGroup === null ) {
 
@@ -502,20 +488,6 @@ THREE.LDrawLoader = ( function () {
 					// Add the subobject just loaded
 					addSubobject( subobject, subobjectGroup );
 
-					// Proceed to load the next subobject, or finish the parent object
-
-					parseScope.subobjectIndex ++;
-
-					if ( parseScope.subobjectIndex < parseScope.numSubobjects ) {
-
-						loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
-
-					} else {
-
-						finalizeObject();
-
-					}
-
 				}
 
 				function addSubobject( subobject, subobjectGroup ) {
@@ -533,10 +505,10 @@ THREE.LDrawLoader = ( function () {
 
 				}
 
-				function onSubobjectError( err ) {
+				function onSubobjectError( err, subobject ) {
 
 					// Retry download from a different default possible location
-					loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
+					loadSubobject( subobject );
 
 				}
 
@@ -1071,7 +1043,7 @@ THREE.LDrawLoader = ( function () {
 					if ( line.startsWith( '0 FILE ' ) ) {
 
 						// Save previous embedded file in the cache
-						this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
+						this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
 
 						// New embedded text file
 						currentEmbeddedFileName = line.substring( 7 );
@@ -1436,7 +1408,7 @@ THREE.LDrawLoader = ( function () {
 
 			if ( parsingEmbeddedFiles ) {
 
-				this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
+				this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
 
 			}
 

+ 11 - 8
examples/js/loaders/LoaderSupport.js

@@ -347,7 +347,6 @@ THREE.LoaderSupport.PrepData.prototype = {
  * @class
  */
 THREE.LoaderSupport.MeshBuilder = function() {
-	console.info( 'Using THREE.LoaderSupport.MeshBuilder version: ' + THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION );
 	this.validator = THREE.LoaderSupport.Validator;
 
 	this.logging = {
@@ -356,9 +355,11 @@ THREE.LoaderSupport.MeshBuilder = function() {
 	};
 
 	this.callbacks = new THREE.LoaderSupport.Callbacks();
-	this.materials = [];
+	this.materials = {};
 };
-THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION = '1.3.0';
+THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION = '1.3.1';
+console.info( 'Using THREE.LoaderSupport.MeshBuilder version: ' + THREE.LoaderSupport.MeshBuilder.LOADER_MESH_BUILDER_VERSION );
+
 
 THREE.LoaderSupport.MeshBuilder.prototype = {
 
@@ -715,7 +716,6 @@ THREE.LoaderSupport.MeshBuilder.prototype = {
  * @class
  */
 THREE.LoaderSupport.WorkerSupport = function () {
-	console.info( 'Using THREE.LoaderSupport.WorkerSupport version: ' + THREE.LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION );
 	this.logging = {
 		enabled: true,
 		debug: false
@@ -726,6 +726,8 @@ THREE.LoaderSupport.WorkerSupport = function () {
 };
 
 THREE.LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION = '2.3.0';
+console.info( 'Using THREE.LoaderSupport.WorkerSupport version: ' + THREE.LoaderSupport.WorkerSupport.WORKER_SUPPORT_VERSION );
+
 
 THREE.LoaderSupport.WorkerSupport.prototype = {
 
@@ -1285,10 +1287,10 @@ THREE.LoaderSupport.WorkerRunnerRefImpl.prototype = {
 
 			var self = this.getParentScope();
 			var callbacks = {
-				callbackMeshBuilder: function ( payload ) {
+				callbackOnAssetAvailable: function ( payload ) {
 					self.postMessage( payload );
 				},
-				callbackProgress: function ( text ) {
+				callbackOnProgress: function ( text ) {
 					if ( payload.logging.enabled && payload.logging.debug ) console.debug( 'WorkerRunner: progress: ' + text );
 				}
 			};
@@ -1304,7 +1306,7 @@ THREE.LoaderSupport.WorkerRunnerRefImpl.prototype = {
 
 			if ( payload.logging.enabled ) console.log( 'WorkerRunner: Run complete!' );
 
-			callbacks.callbackMeshBuilder( {
+			callbacks.callbackOnAssetAvailable( {
 				cmd: 'complete',
 				msg: 'WorkerRunner completed run.'
 			} );
@@ -1414,7 +1416,6 @@ THREE.LoaderSupport.WorkerSupport.NodeLoaderWorker.prototype.initWorker = functi
  * @param {string} classDef Class definition to be used for construction
  */
 THREE.LoaderSupport.WorkerDirector = function ( classDef ) {
-	console.info( 'Using THREE.LoaderSupport.WorkerDirector version: ' + THREE.LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION );
 	this.logging = {
 		enabled: true,
 		debug: false
@@ -1443,6 +1444,8 @@ THREE.LoaderSupport.WorkerDirector = function ( classDef ) {
 THREE.LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION = '2.3.0';
 THREE.LoaderSupport.WorkerDirector.MAX_WEB_WORKER = 16;
 THREE.LoaderSupport.WorkerDirector.MAX_QUEUE_SIZE = 2048;
+console.info( 'Using THREE.LoaderSupport.WorkerDirector version: ' + THREE.LoaderSupport.WorkerDirector.LOADER_WORKER_DIRECTOR_VERSION );
+
 
 THREE.LoaderSupport.WorkerDirector.prototype = {
 

+ 117 - 74
examples/js/loaders/OBJLoader2.js

@@ -17,8 +17,6 @@ if ( THREE.LoaderSupport === undefined ) console.error( '"THREE.LoaderSupport" i
  */
 
 THREE.OBJLoader2 = function ( manager ) {
-	console.info( 'Using THREE.OBJLoader2 version: ' + THREE.OBJLoader2.OBJLOADER2_VERSION );
-
 	this.manager = THREE.LoaderSupport.Validator.verifyInput( manager, THREE.DefaultLoadingManager );
 	this.logging = {
 		enabled: true,
@@ -40,8 +38,9 @@ THREE.OBJLoader2 = function ( manager ) {
 	this.workerSupport = new THREE.LoaderSupport.WorkerSupport();
 	this.terminateWorkerOnLoad = true;
 };
+THREE.OBJLoader2.OBJLOADER2_VERSION = '2.5.1';
+console.info( 'Using THREE.OBJLoader2 version: ' + THREE.OBJLoader2.OBJLOADER2_VERSION );
 
-THREE.OBJLoader2.OBJLOADER2_VERSION = '2.5.0';
 
 THREE.OBJLoader2.prototype = {
 
@@ -174,19 +173,12 @@ THREE.OBJLoader2.prototype = {
 		if ( this.logging.enabled && this.logging.debug ) console.debug( content );
 	},
 
-	_onError: function ( event ) {
-		var output = 'Error occurred while downloading!';
-
-		if ( event.currentTarget && event.currentTarget.statusText !== null ) {
+	_onError: function ( errorMessage ) {
+		if ( this.logging.enabled && this.logging.debug ) {
 
-			output += '\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;
+			console.log( errorMessage );
 
 		}
-		this.onProgress( 'error', output, -1 );
-		this._throwError( output );
-	},
-
-	_throwError: function ( errorMessage ) {
 		if ( THREE.LoaderSupport.Validator.isValid( this.callbacks.onReportError ) )  {
 
 			this.callbacks.onReportError( errorMessage );
@@ -216,9 +208,18 @@ THREE.OBJLoader2.prototype = {
 	_loadObj: function ( resource, onLoad, onProgress, onError, onMeshAlter, useAsync ) {
 		var scope = this;
 		if ( ! THREE.LoaderSupport.Validator.isValid( onError ) ) {
+
 			onError = function ( event ) {
-				scope._onError( event );
-			}
+
+				var errorMessage = event;
+				if ( event.currentTarget && event.currentTarget.statusText !== null ) {
+
+					errorMessage = 'Error occurred while downloading!\nurl: ' + event.currentTarget.responseURL + '\nstatus: ' + event.currentTarget.statusText;
+
+				}
+				scope._onError( errorMessage );
+
+			};
 		}
 
 		// fast-fail
@@ -339,10 +340,9 @@ THREE.OBJLoader2.prototype = {
 	 */
 	parse: function ( content ) {
 		// fast-fail in case of illegal data
-		if ( ! THREE.LoaderSupport.Validator.isValid( content ) ) {
+		if ( content === null || content === undefined ) {
 
-			console.warn( 'Provided content is not a valid ArrayBuffer or String.' );
-			return this.loaderRootNode;
+			throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
 
 		}
 		if ( this.logging.enabled ) console.time( 'OBJLoader2 parse: ' + this.modelName );
@@ -366,11 +366,15 @@ THREE.OBJLoader2.prototype = {
 				scope.loaderRootNode.add( mesh );
 			}
 		};
-		parser.setCallbackMeshBuilder( onMeshLoaded );
+		parser.setCallbackOnAssetAvailable( onMeshLoaded );
 		var onProgressScoped = function ( text, numericalValue ) {
 			scope.onProgress( 'progressParse', text, numericalValue );
 		};
-		parser.setCallbackProgress( onProgressScoped );
+		parser.setCallbackOnProgress( onProgressScoped );
+		var onErrorScoped = function ( message ) {
+			scope._onError( message );
+		};
+		parser.setCallbackOnError( onErrorScoped );
 
 		if ( content instanceof ArrayBuffer || content instanceof Uint8Array ) {
 
@@ -384,7 +388,7 @@ THREE.OBJLoader2.prototype = {
 
 		} else {
 
-			this._throwError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );
+			this._onError( 'Provided content was neither of type String nor Uint8Array! Aborting...' );
 
 		}
 		if ( this.logging.enabled ) console.timeEnd( 'OBJLoader2 parse: ' + this.modelName );
@@ -414,10 +418,9 @@ THREE.OBJLoader2.prototype = {
 			if ( measureTime && scope.logging.enabled ) console.timeEnd( 'OBJLoader2 parseAsync: ' + scope.modelName );
 		};
 		// fast-fail in case of illegal data
-		if ( ! THREE.LoaderSupport.Validator.isValid( content ) ) {
+		if ( content === null || content === undefined ) {
 
-			console.warn( 'Provided content is not a valid ArrayBuffer.' );
-			scopedOnLoad()
+			throw 'Provided content is not a valid ArrayBuffer or String. Unable to continue parsing';
 
 		} else {
 
@@ -441,7 +444,6 @@ THREE.OBJLoader2.prototype = {
 			workerCode += '  * This code was constructed by OBJLoader2 buildCode.\n';
 			workerCode += '  */\n\n';
 			workerCode += 'THREE = { LoaderSupport: {}, OBJLoader2: {} };\n\n';
-			workerCode += codeSerializer.serializeObject( 'THREE.LoaderSupport.Validator', THREE.LoaderSupport.Validator );
 			workerCode += codeSerializer.serializeClass( 'THREE.OBJLoader2.Parser', THREE.OBJLoader2.Parser );
 
 			return workerCode;
@@ -503,7 +505,7 @@ THREE.OBJLoader2.prototype = {
 		if ( THREE.MTLLoader === undefined ) console.error( '"THREE.MTLLoader" is not available. "THREE.OBJLoader2" requires it for loading MTL files.' );
 		if ( THREE.LoaderSupport.Validator.isValid( resource ) && this.logging.enabled ) console.time( 'Loading MTL: ' + resource.name );
 
-		var materials = [];
+		var materials = {};
 		var scope = this;
 		var processMaterials = function ( materialCreator ) {
 			var materialCreatorMaterials = [];
@@ -548,7 +550,7 @@ THREE.OBJLoader2.prototype = {
 
 					} else {
 
-						this._throwError( 'Unable to parse mtl as it it seems to be neither a String, an Array or an ArrayBuffer!' );
+						scope._onError( 'Unable to parse mtl as it it seems to be neither a String, an Array or an ArrayBuffer!' );
 					}
 
 				}
@@ -597,8 +599,11 @@ THREE.OBJLoader2.prototype = {
  * @class
  */
 THREE.OBJLoader2.Parser = function () {
-	this.callbackProgress = null;
-	this.callbackMeshBuilder = null;
+	this.callbacks = {
+		onProgress: null,
+		onAssetAvailable: null,
+		onError: null
+	};
 	this.contentRef = null;
 	this.legacyMode = false;
 
@@ -696,29 +701,61 @@ THREE.OBJLoader2.Parser.prototype = {
 	},
 
 	setMaterials: function ( materials ) {
-		this.materials = THREE.LoaderSupport.Validator.verifyInput( materials, this.materials );
-		this.materials = THREE.LoaderSupport.Validator.verifyInput( this.materials, {} );
+		if ( materials === undefined || materials === null ) return;
+
+		for ( var materialName in materials ) {
+			if ( materials.hasOwnProperty( materialName ) ) {
+
+				this.materials[ materialName ] = materials[ materialName ];
+
+			}
+		}
 	},
 
-	setCallbackMeshBuilder: function ( callbackMeshBuilder ) {
-		if ( ! THREE.LoaderSupport.Validator.isValid( callbackMeshBuilder ) ) {
+	setCallbackOnAssetAvailable: function ( onAssetAvailable ) {
+		if ( onAssetAvailable !== null && onAssetAvailable !== undefined ) {
 
-			this._throwError( 'Unable to run as no "MeshBuilder" callback is set.' );
+			this.callbacks.onAssetAvailable = onAssetAvailable;
 
 		}
-		this.callbackMeshBuilder = callbackMeshBuilder;
 	},
 
-	setCallbackProgress: function ( callbackProgress ) {
-		this.callbackProgress = callbackProgress;
+	setCallbackOnProgress: function ( onProgress ) {
+		if ( onProgress !== null && onProgress !== undefined ) {
+
+			this.callbacks.onProgress = onProgress;
+
+		}
+	},
+
+	setCallbackOnError: function ( onError ) {
+		if ( onError !== null && onError !== undefined ) {
+
+			this.callbacks.onError = onError;
+
+		}
 	},
 
+
 	setLogging: function ( enabled, debug ) {
 		this.logging.enabled = enabled === true;
 		this.logging.debug = debug === true;
 	},
 
 	configure: function () {
+		if ( this.callbacks.onAssetAvailable === null ) {
+
+			var errorMessage = 'Unable to run as no callback for building meshes is set.';
+			if ( this.callbacks.onError !== null ) {
+
+				this.callbacks.onError( errorMessage );
+
+			} else {
+
+				throw errorMessage;
+			}
+
+		}
 		this.pushSmoothingGroup( 1 );
 
 		if ( this.logging.enabled ) {
@@ -731,9 +768,16 @@ THREE.OBJLoader2.Parser.prototype = {
 				+ '\n\tmaterialPerSmoothingGroup: ' + this.materialPerSmoothingGroup
 				+ '\n\tuseOAsMesh: ' + this.useOAsMesh
 				+ '\n\tuseIndices: ' + this.useIndices
-				+ '\n\tdisregardNormals: ' + this.disregardNormals
-				+ '\n\tcallbackMeshBuilderName: ' + this.callbackMeshBuilder.name
-				+ '\n\tcallbackProgressName: ' + this.callbackProgress.name;
+				+ '\n\tdisregardNormals: ' + this.disregardNormals;
+			if ( this.callbacks.onProgress !== null ) {
+				printedConfig += '\n\tcallbacks.onProgress: ' + this.callbacks.onProgress.name;
+			}
+			if ( this.callbacks.onAssetAvailable !== null ) {
+				printedConfig += '\n\tcallbacks.onAssetAvailable: ' + this.callbacks.onAssetAvailable.name;
+			}
+			if ( this.callbacks.onError !== null ) {
+				printedConfig += '\n\tcallbacks.onError: ' + this.callbacks.onError.name;
+			}
 			console.info( printedConfig );
 		}
 	},
@@ -1042,7 +1086,7 @@ THREE.OBJLoader2.Parser.prototype = {
 		var index = this.rawMesh.activeMtlName + '|' + this.rawMesh.smoothingGroup.normalized;
 		this.rawMesh.subGroupInUse = this.rawMesh.subGroups[ index ];
 
-		if ( ! THREE.LoaderSupport.Validator.isValid( this.rawMesh.subGroupInUse ) ) {
+		if ( this.rawMesh.subGroupInUse === undefined || this.rawMesh.subGroupInUse === null ) {
 
 			this.rawMesh.subGroupInUse = {
 				index: index,
@@ -1110,17 +1154,17 @@ THREE.OBJLoader2.Parser.prototype = {
 
 			var mappingName = faceIndexV + ( faceIndexU ? '_' + faceIndexU : '_n' ) + ( faceIndexN ? '_' + faceIndexN : '_n' );
 			var indicesPointer = this.rawMesh.subGroupInUse.indexMappings[ mappingName ];
-			if ( THREE.LoaderSupport.Validator.isValid( indicesPointer ) ) {
-
-				this.rawMesh.counts.doubleIndicesCount++;
-
-			} else {
+			if ( indicesPointer === undefined || indicesPointer === null ) {
 
 				indicesPointer = this.rawMesh.subGroupInUse.vertices.length / 3;
 				updateSubGroupInUse();
 				this.rawMesh.subGroupInUse.indexMappings[ mappingName ] = indicesPointer;
 				this.rawMesh.subGroupInUse.indexMappingsCount++;
 
+			} else {
+
+				this.rawMesh.counts.doubleIndicesCount++;
+
 			}
 			this.rawMesh.subGroupInUse.indices.push( indicesPointer );
 
@@ -1202,11 +1246,12 @@ THREE.OBJLoader2.Parser.prototype = {
 
 	processCompletedMesh: function () {
 		var result = this.finalizeRawMesh();
-		if ( THREE.LoaderSupport.Validator.isValid( result ) ) {
+		var haveMesh = result !== null;
+		if ( haveMesh ) {
 
-			if ( this.colors.length > 0 && this.colors.length !== this.vertices.length ) {
+			if ( this.colors.length > 0 && this.colors.length !== this.vertices.length && this.callbacks.onError !== null ) {
 
-				this._throwError( 'Vertex Colors were detected, but vertex count and color count do not match!' );
+				this.callbacks.onError( 'Vertex Colors were detected, but vertex count and color count do not match!' );
 
 			}
 			if ( this.logging.enabled && this.logging.debug ) console.debug( this.createRawMeshReport( this.inputObjectCount ) );
@@ -1214,14 +1259,17 @@ THREE.OBJLoader2.Parser.prototype = {
 
 			this.buildMesh( result );
 			var progressBytesPercent = this.globalCounts.currentByte / this.globalCounts.totalBytes;
-			this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
+			if ( this.callbacks.onProgress !== null ) {
+
+				this.callbacks.onProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName +
+					'] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
+
+			}
 			this.resetRawMesh();
 			return true;
 
-		} else {
-
-			return false;
 		}
+		return haveMesh;
 	},
 
 	/**
@@ -1241,7 +1289,7 @@ THREE.OBJLoader2.Parser.prototype = {
 		var colorFA = ( result.absoluteColorCount > 0 ) ? new Float32Array( result.absoluteColorCount ) : null;
 		var normalFA = ( result.absoluteNormalCount > 0 ) ? new Float32Array( result.absoluteNormalCount ) : null;
 		var uvFA = ( result.absoluteUvCount > 0 ) ? new Float32Array( result.absoluteUvCount ) : null;
-		var haveVertexColors = THREE.LoaderSupport.Validator.isValid( colorFA );
+		var haveVertexColors = colorFA !== null;
 
 		var meshOutputGroup;
 		var materialNames = [];
@@ -1283,25 +1331,20 @@ THREE.OBJLoader2.Parser.prototype = {
 			material = this.materials[ materialName ];
 
 			// both original and derived names do not lead to an existing material => need to use a default material
-			if ( ! THREE.LoaderSupport.Validator.isValid( materialOrg ) && ! THREE.LoaderSupport.Validator.isValid( material ) ) {
-
-				var defaultMaterialName = haveVertexColors ? 'defaultVertexColorMaterial' : 'defaultMaterial';
-				materialOrg = this.materials[ defaultMaterialName ];
-				if ( this.logging.enabled ) console.warn( 'object_group "' + meshOutputGroup.objectName + '_' +
-					meshOutputGroup.groupName + '" was defined with unresolvable material "' +
-					materialNameOrg + '"! Assigning "' + defaultMaterialName + '".' );
-				materialNameOrg = defaultMaterialName;
+			if ( ( materialOrg === undefined || materialOrg === null ) && ( material === undefined || material === null ) ) {
 
-				// if names are identical then there is no need for later manipulation
-				if ( materialNameOrg === materialName ) {
+				materialName = haveVertexColors ? 'defaultVertexColorMaterial' : 'defaultMaterial';
+				material = this.materials[ materialName ];
+				if ( this.logging.enabled ) {
 
-					material = materialOrg;
-					materialName = defaultMaterialName;
+					console.info( 'object_group "' + meshOutputGroup.objectName + '_' +
+						meshOutputGroup.groupName + '" was defined with unresolvable material "' +
+						materialNameOrg + '"! Assigning "' + materialName + '".' );
 
 				}
 
 			}
-			if ( ! THREE.LoaderSupport.Validator.isValid( material ) ) {
+			if ( material === undefined || material === null ) {
 
 				var materialCloneInstructions = {
 					materialNameOrg: materialNameOrg,
@@ -1317,7 +1360,7 @@ THREE.OBJLoader2.Parser.prototype = {
 						materialCloneInstructions: materialCloneInstructions
 					}
 				};
-				this.callbackMeshBuilder( payload );
+				this.callbacks.onAssetAvailable( payload );
 
 				// fake entry for async; sync Parser always works on material references (Builder update directly visible here)
 				if ( this.useAsync ) this.materials[ materialName ] = materialCloneInstructions;
@@ -1382,7 +1425,7 @@ THREE.OBJLoader2.Parser.prototype = {
 			}
 
 			if ( this.logging.enabled && this.logging.debug ) {
-				var materialIndexLine = THREE.LoaderSupport.Validator.isValid( selectedMaterialIndex ) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : '';
+				var materialIndexLine = ( selectedMaterialIndex === undefined || selectedMaterialIndex === null ) ? '' : '\n\t\tmaterialIndex: ' + selectedMaterialIndex;
 				var createdReport = '\tOutput Object no.: ' + this.outputObjectCount +
 					'\n\t\tgroupName: ' + meshOutputGroup.groupName +
 					'\n\t\tIndex: ' + meshOutputGroup.index +
@@ -1402,7 +1445,7 @@ THREE.OBJLoader2.Parser.prototype = {
 		}
 
 		this.outputObjectCount++;
-		this.callbackMeshBuilder(
+		this.callbacks.onAssetAvailable(
 			{
 				cmd: 'meshData',
 				progress: {
@@ -1427,10 +1470,10 @@ THREE.OBJLoader2.Parser.prototype = {
 				geometryType: this.rawMesh.faceType < 4 ? 0 : ( this.rawMesh.faceType === 6 ) ? 2 : 1
 			},
 			[ vertexFA.buffer ],
-			THREE.LoaderSupport.Validator.isValid( indexUA ) ? [ indexUA.buffer ] : null,
-			THREE.LoaderSupport.Validator.isValid( colorFA ) ? [ colorFA.buffer ] : null,
-			THREE.LoaderSupport.Validator.isValid( normalFA ) ? [ normalFA.buffer ] : null,
-			THREE.LoaderSupport.Validator.isValid( uvFA ) ? [ uvFA.buffer ] : null
+			indexUA !== null ?  [ indexUA.buffer ] : null,
+			colorFA !== null ? [ colorFA.buffer ] : null,
+			normalFA !== null ? [ normalFA.buffer ] : null,
+			uvFA !== null ? [ uvFA.buffer ] : null
 		);
 	},
 

+ 1 - 1
examples/js/loaders/PCDLoader.js

@@ -169,7 +169,7 @@ THREE.PCDLoader.prototype = {
 
 		}
 
-		var textData = THREE.LoaderUtils.decodeText( data );
+		var textData = THREE.LoaderUtils.decodeText( new Uint8Array( data ) );
 
 		// parse header (always ascii format)
 

+ 7 - 4
examples/js/loaders/SVGLoader.js

@@ -804,13 +804,16 @@ THREE.SVGLoader.prototype = {
 
 			var transform = new THREE.Matrix3();
 			var currentTransform = tempTransform0;
-			var transformsTexts = node.getAttribute( 'transform' ).split( ' ' );
+			var transformsTexts = node.getAttribute( 'transform' ).split( ')' );
 
 			for ( var tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex -- ) {
 
-				var transformText = transformsTexts[ tIndex ];
-				var openParPos = transformText.indexOf( "(" );
-				var closeParPos = transformText.indexOf( ")" );
+				var transformText = transformsTexts[ tIndex ].trim();
+
+				if ( transformText === '' ) continue;
+
+				var openParPos = transformText.indexOf( '(' );
+				var closeParPos = transformText.length;
 
 				if ( openParPos > 0 && openParPos < closeParPos ) {
 

+ 3 - 3
examples/js/math/ColorConverter.js

@@ -17,7 +17,7 @@ THREE.ColorConverter = {
 
 	},
 
-	getHSV: function() {
+	getHSV: function () {
 
 		var hsl = {};
 
@@ -46,7 +46,7 @@ THREE.ColorConverter = {
 	}(),
 
 	// where c, m, y, k is between 0 and 1
-	
+
 	setCMYK: function ( color, c, m, y, k ) {
 
 		var r = ( 1 - c ) * ( 1 - k );
@@ -62,7 +62,7 @@ THREE.ColorConverter = {
 		if ( target === undefined ) {
 
 			console.warn( 'THREE.ColorConverter: .getCMYK() target is now required' );
-			target = { c: 0, m: 0, y: 0, k:0 };
+			target = { c: 0, m: 0, y: 0, k: 0 };
 
 		}
 

+ 59 - 355
examples/js/math/Lut.js

@@ -5,43 +5,30 @@
 THREE.Lut = function ( colormap, numberofcolors ) {
 
 	this.lut = [];
-	this.map = THREE.ColorMapKeywords[ colormap ];
-	this.n = numberofcolors;
-	this.mapname = colormap;
+	this.setColorMap( colormap, numberofcolors );
+	return this;
 
-	var step = 1.0 / this.n;
-
-	for ( var i = 0; i <= 1; i += step ) {
-
-		for ( var j = 0; j < this.map.length - 1; j ++ ) {
-
-			if ( i >= this.map[ j ][ 0 ] && i < this.map[ j + 1 ][ 0 ] ) {
-
-				var min = this.map[ j ][ 0 ];
-				var max = this.map[ j + 1 ][ 0 ];
-
-				var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] );
-				var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j + 1 ][ 1 ] );
-
-				var color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
-
-				this.lut.push( color );
-
-			}
-
-		}
-
-	}
-
-	return this.set( this );
+};
 
+var defaultLabelParameters = {
+	fontsize: 24,
+	fontface: 'Arial',
+	title: '',
+	um: '',
+	ticks: 0,
+	decimal: 2,
+	notation: 'standard'
 };
 
+var defaultBackgroundColor = { r: 255, g: 100, b: 100, a: 0.8 };
+var defaultBorderColor = { r: 255, g: 0, b: 0, a: 1.0 };
+var defaultBorderThickness = 4;
+
 THREE.Lut.prototype = {
 
 	constructor: THREE.Lut,
 
-	lut: [], map: [], mapname: 'rainbow', n: 256, minV: 0, maxV: 1, legend: null,
+	lut: [], map: [], n: 256, minV: 0, maxV: 1,
 
 	set: function ( value ) {
 
@@ -71,26 +58,43 @@ THREE.Lut.prototype = {
 
 	},
 
-	changeNumberOfColors: function ( numberofcolors ) {
+	setColorMap: function ( colormap, numberofcolors ) {
 
-		this.n = numberofcolors;
+		this.map = THREE.ColorMapKeywords[ colormap ] || THREE.ColorMapKeywords.rainbow;
+		this.n = numberofcolors || 32;
 
-		return new THREE.Lut( this.mapname, this.n );
+		var step = 1.0 / this.n;
 
-	},
+		this.lut.length = 0;
+		for ( var i = 0; i <= 1; i += step ) {
+
+			for ( var j = 0; j < this.map.length - 1; j ++ ) {
+
+				if ( i >= this.map[ j ][ 0 ] && i < this.map[ j + 1 ][ 0 ] ) {
 
-	changeColorMap: function ( colormap ) {
+					var min = this.map[ j ][ 0 ];
+					var max = this.map[ j + 1 ][ 0 ];
 
-		this.mapname = colormap;
+					var minColor = new THREE.Color( this.map[ j ][ 1 ] );
+					var maxColor = new THREE.Color( this.map[ j + 1 ][ 1 ] );
 
-		return new THREE.Lut( this.mapname, this.n );
+					var color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
+
+					this.lut.push( color );
+
+				}
+
+			}
+
+		}
+
+		return this;
 
 	},
 
 	copy: function ( lut ) {
 
 		this.lut = lut.lut;
-		this.mapname = lut.mapname;
 		this.map = lut.map;
 		this.n = lut.n;
 		this.minV = lut.minV;
@@ -127,42 +131,26 @@ THREE.Lut.prototype = {
 
 	},
 
-	setLegendOn: function ( parameters ) {
+	createCanvas: function () {
 
-		if ( parameters === undefined ) {
-
-			parameters = {};
-
-		}
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = 1;
+		canvas.height = this.n;
 
-		this.legend = {};
+		this.updateCanvas( canvas );
 
-		this.legend.layout = parameters.hasOwnProperty( 'layout' ) ? parameters[ 'layout' ] : 'vertical';
+		return canvas;
 
-		this.legend.position = parameters.hasOwnProperty( 'position' ) ? parameters[ 'position' ] : { 'x': 4, 'y': 0, 'z': 0 };
-
-		this.legend.dimensions = parameters.hasOwnProperty( 'dimensions' ) ? parameters[ 'dimensions' ] : { 'width': 0.5, 'height': 3 };
-
-		this.legend.canvas = document.createElement( 'canvas' );
-
-		this.legend.canvas.setAttribute( 'id', 'legend' );
-		this.legend.canvas.setAttribute( 'hidden', true );
-
-		document.body.appendChild( this.legend.canvas );
-
-		this.legend.ctx = this.legend.canvas.getContext( '2d' );
+	},
 
-		this.legend.canvas.setAttribute( 'width', 1 );
-		this.legend.canvas.setAttribute( 'height', this.n );
+	updateCanvas: function ( canvas ) {
 
-		this.legend.texture = new THREE.Texture( this.legend.canvas );
+		var ctx = canvas.getContext( '2d', { alpha: false } );
 
-		var imageData = this.legend.ctx.getImageData( 0, 0, 1, this.n );
+		var imageData = ctx.getImageData( 0, 0, 1, this.n );
 
 		var data = imageData.data;
 
-		this.map = THREE.ColorMapKeywords[ this.mapname ];
-
 		var k = 0;
 
 		var step = 1.0 / this.n;
@@ -176,8 +164,8 @@ THREE.Lut.prototype = {
 					var min = this.map[ j - 1 ][ 0 ];
 					var max = this.map[ j ][ 0 ];
 
-					var minColor = new THREE.Color( 0xffffff ).setHex( this.map[ j - 1 ][ 1 ] );
-					var maxColor = new THREE.Color( 0xffffff ).setHex( this.map[ j ][ 1 ] );
+					var minColor = new THREE.Color( this.map[ j - 1 ][ 1 ] );
+					var maxColor = new THREE.Color( this.map[ j ][ 1 ] );
 
 					var color = minColor.lerp( maxColor, ( i - min ) / ( max - min ) );
 
@@ -194,303 +182,19 @@ THREE.Lut.prototype = {
 
 		}
 
-		this.legend.ctx.putImageData( imageData, 0, 0 );
-		this.legend.texture.needsUpdate = true;
-
-		this.legend.legendGeometry = new THREE.PlaneBufferGeometry( this.legend.dimensions.width, this.legend.dimensions.height );
-		this.legend.legendMaterial = new THREE.MeshBasicMaterial( { map: this.legend.texture, side: THREE.DoubleSide } );
-
-		this.legend.mesh = new THREE.Mesh( this.legend.legendGeometry, this.legend.legendMaterial );
-
-		if ( this.legend.layout == 'horizontal' ) {
-
-			this.legend.mesh.rotation.z = - 90 * ( Math.PI / 180 );
-
-		}
-
-		this.legend.mesh.position.copy( this.legend.position );
-
-		return this.legend.mesh;
-
-	},
-
-	setLegendOff: function () {
-
-		this.legend = null;
-
-		return this.legend;
-
-	},
-
-	setLegendLayout: function ( layout ) {
-
-		if ( ! this.legend ) {
-
-			return false;
-
-		}
-
-		if ( this.legend.layout == layout ) {
-
-			return false;
-
-		}
-
-		if ( layout != 'horizontal' && layout != 'vertical' ) {
-
-			return false;
-
-		}
-
-		this.layout = layout;
-
-		if ( layout == 'horizontal' ) {
-
-			this.legend.mesh.rotation.z = 90 * ( Math.PI / 180 );
-
-		}
-
-		if ( layout == 'vertical' ) {
-
-			this.legend.mesh.rotation.z = - 90 * ( Math.PI / 180 );
-
-		}
-
-		return this.legend.mesh;
-
-	},
-
-	setLegendPosition: function ( position ) {
-
-		this.legend.position = new THREE.Vector3( position.x, position.y, position.z );
-
-		return this.legend;
-
-	},
-
-	setLegendLabels: function ( parameters, callback ) {
-
-		if ( ! this.legend ) {
-
-			return false;
-
-		}
-
-		if ( typeof parameters === 'function' ) {
-
-			callback = parameters;
-
-		}
-
-		if ( parameters === undefined ) {
-
-			parameters = {};
-
-		}
-
-		this.legend.labels = {};
-
-		this.legend.labels.fontsize = parameters.hasOwnProperty( 'fontsize' ) ? parameters[ 'fontsize' ] : 24;
-
-		this.legend.labels.fontface = parameters.hasOwnProperty( 'fontface' ) ? parameters[ 'fontface' ] : 'Arial';
+		ctx.putImageData( imageData, 0, 0 );
 
-		this.legend.labels.title = parameters.hasOwnProperty( 'title' ) ? parameters[ 'title' ] : '';
-
-		this.legend.labels.um = parameters.hasOwnProperty( 'um' ) ? ' [ ' + parameters[ 'um' ] + ' ]' : '';
-
-		this.legend.labels.ticks = parameters.hasOwnProperty( 'ticks' ) ? parameters[ 'ticks' ] : 0;
-
-		this.legend.labels.decimal = parameters.hasOwnProperty( 'decimal' ) ? parameters[ 'decimal' ] : 2;
-
-		this.legend.labels.notation = parameters.hasOwnProperty( 'notation' ) ? parameters[ 'notation' ] : 'standard';
-
-		var backgroundColor = { r: 255, g: 100, b: 100, a: 0.8 };
-		var borderColor = { r: 255, g: 0, b: 0, a: 1.0 };
-		var borderThickness = 4;
-
-		var canvasTitle = document.createElement( 'canvas' );
-		var contextTitle = canvasTitle.getContext( '2d' );
-
-		contextTitle.font = 'Normal ' + this.legend.labels.fontsize * 1.2 + 'px ' + this.legend.labels.fontface;
-
-		contextTitle.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')';
-
-		contextTitle.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')';
-
-		contextTitle.lineWidth = borderThickness;
-
-		contextTitle.fillStyle = 'rgba( 0, 0, 0, 1.0 )';
-
-		contextTitle.fillText( this.legend.labels.title.toString() + this.legend.labels.um.toString(), borderThickness, this.legend.labels.fontsize + borderThickness );
-
-		var txtTitle = new THREE.CanvasTexture( canvasTitle );
-		txtTitle.minFilter = THREE.LinearFilter;
-
-		var spriteMaterialTitle = new THREE.SpriteMaterial( { map: txtTitle } );
-
-		var spriteTitle = new THREE.Sprite( spriteMaterialTitle );
-
-		spriteTitle.scale.set( 2, 1, 1.0 );
-
-		if ( this.legend.layout == 'vertical' ) {
-
-			spriteTitle.position.set( this.legend.position.x + this.legend.dimensions.width, this.legend.position.y + ( this.legend.dimensions.height * 0.45 ), this.legend.position.z );
-
-		}
-
-		if ( this.legend.layout == 'horizontal' ) {
-
-			spriteTitle.position.set( this.legend.position.x * 1.015, this.legend.position.y + ( this.legend.dimensions.height * 0.03 ), this.legend.position.z );
-
-		}
-
-		if ( this.legend.labels.ticks > 0 ) {
-
-			var ticks = {};
-			var lines = {};
-
-			if ( this.legend.layout == 'vertical' ) {
-
-				var topPositionY = this.legend.position.y + ( this.legend.dimensions.height * 0.36 );
-				var bottomPositionY = this.legend.position.y - ( this.legend.dimensions.height * 0.61 );
-
-			}
-
-			if ( this.legend.layout == 'horizontal' ) {
-
-				var topPositionX = this.legend.position.x + ( this.legend.dimensions.height * 0.75 );
-				var bottomPositionX = this.legend.position.x - ( this.legend.dimensions.width * 1.2 );
-
-			}
-
-			for ( var i = 0; i < this.legend.labels.ticks; i ++ ) {
-
-				var value = ( this.maxV - this.minV ) / ( this.legend.labels.ticks - 1 ) * i + this.minV;
-
-				if ( callback ) {
-
-					value = callback( value );
-
-				} else {
-
-					if ( this.legend.labels.notation == 'scientific' ) {
-
-						value = value.toExponential( this.legend.labels.decimal );
-
-					} else {
-
-						value = value.toFixed( this.legend.labels.decimal );
-
-					}
-
-				}
-
-				var canvasTick = document.createElement( 'canvas' );
-				var contextTick = canvasTick.getContext( '2d' );
-
-				contextTick.font = 'Normal ' + this.legend.labels.fontsize + 'px ' + this.legend.labels.fontface;
-
-				contextTick.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + backgroundColor.a + ')';
-
-				contextTick.strokeStyle = 'rgba(' + borderColor.r + ',' + borderColor.g + ',' + borderColor.b + ',' + borderColor.a + ')';
-
-				contextTick.lineWidth = borderThickness;
-
-				contextTick.fillStyle = 'rgba( 0, 0, 0, 1.0 )';
-
-				contextTick.fillText( value.toString(), borderThickness, this.legend.labels.fontsize + borderThickness );
-
-				var txtTick = new THREE.CanvasTexture( canvasTick );
-				txtTick.minFilter = THREE.LinearFilter;
-
-				var spriteMaterialTick = new THREE.SpriteMaterial( { map: txtTick } );
-
-				var spriteTick = new THREE.Sprite( spriteMaterialTick );
-
-				spriteTick.scale.set( 2, 1, 1.0 );
-
-				if ( this.legend.layout == 'vertical' ) {
-
-					var position = bottomPositionY + ( topPositionY - bottomPositionY ) * ( ( value - this.minV ) / ( this.maxV - this.minV ) );
-
-					spriteTick.position.set( this.legend.position.x + ( this.legend.dimensions.width * 2.7 ), position, this.legend.position.z );
-
-				}
-
-				if ( this.legend.layout == 'horizontal' ) {
-
-					var position = bottomPositionX + ( topPositionX - bottomPositionX ) * ( ( value - this.minV ) / ( this.maxV - this.minV ) );
-
-					if ( this.legend.labels.ticks > 5 ) {
-
-						if ( i % 2 === 0 ) {
-
-							var offset = 1.7;
-
-						} else {
-
-							var offset = 2.1;
-
-						}
-
-					} else {
-
-						var offset = 1.7;
-
-					}
-
-					spriteTick.position.set( position, this.legend.position.y - this.legend.dimensions.width * offset, this.legend.position.z );
-
-				}
-
-				var material = new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } );
-
-				var points = [];
-
-
-				if ( this.legend.layout == 'vertical' ) {
-
-					var linePosition = ( this.legend.position.y - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( ( value - this.minV ) / ( this.maxV - this.minV ) * 0.99 );
-
-					points.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.55, linePosition, this.legend.position.z ) );
-
-					points.push( new THREE.Vector3( this.legend.position.x + this.legend.dimensions.width * 0.7, linePosition, this.legend.position.z ) );
-
-				}
-
-				if ( this.legend.layout == 'horizontal' ) {
-
-					var linePosition = ( this.legend.position.x - ( this.legend.dimensions.height * 0.5 ) + 0.01 ) + ( this.legend.dimensions.height ) * ( ( value - this.minV ) / ( this.maxV - this.minV ) * 0.99 );
-
-					points.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.55, this.legend.position.z ) );
-
-					points.push( new THREE.Vector3( linePosition, this.legend.position.y - this.legend.dimensions.width * 0.7, this.legend.position.z ) );
-
-				}
-
-				var geometry = new THREE.BufferGeometry().setFromPoints( points );
-
-				var line = new THREE.Line( geometry, material );
-
-				lines[ i ] = line;
-				ticks[ i ] = spriteTick;
-
-			}
-
-		}
-
-		return { 'title': spriteTitle, 'ticks': ticks, 'lines': lines };
+		return canvas;
 
 	}
-
 };
 
 
 THREE.ColorMapKeywords = {
 
-	"rainbow": [[ 0.0, '0x0000FF' ], [ 0.2, '0x00FFFF' ], [ 0.5, '0x00FF00' ], [ 0.8, '0xFFFF00' ], [ 1.0, '0xFF0000' ]],
-	"cooltowarm": [[ 0.0, '0x3C4EC2' ], [ 0.2, '0x9BBCFF' ], [ 0.5, '0xDCDCDC' ], [ 0.8, '0xF6A385' ], [ 1.0, '0xB40426' ]],
-	"blackbody": [[ 0.0, '0x000000' ], [ 0.2, '0x780000' ], [ 0.5, '0xE63200' ], [ 0.8, '0xFFFF00' ], [ 1.0, '0xFFFFFF' ]],
-	"grayscale": [[ 0.0, '0x000000' ], [ 0.2, '0x404040' ], [ 0.5, '0x7F7F80' ], [ 0.8, '0xBFBFBF' ], [ 1.0, '0xFFFFFF' ]]
+	"rainbow": [[ 0.0, 0x0000FF ], [ 0.2, 0x00FFFF ], [ 0.5, 0x00FF00 ], [ 0.8, 0xFFFF00 ], [ 1.0, 0xFF0000 ]],
+	"cooltowarm": [[ 0.0, 0x3C4EC2 ], [ 0.2, 0x9BBCFF ], [ 0.5, 0xDCDCDC ], [ 0.8, 0xF6A385 ], [ 1.0, 0xB40426 ]],
+	"blackbody": [[ 0.0, 0x000000 ], [ 0.2, 0x780000 ], [ 0.5, 0xE63200 ], [ 0.8, 0xFFFF00 ], [ 1.0, 0xFFFFFF ]],
+	"grayscale": [[ 0.0, 0x000000 ], [ 0.2, 0x404040 ], [ 0.5, 0x7F7F80 ], [ 0.8, 0xBFBFBF ], [ 1.0, 0xFFFFFF ]]
 
 };

+ 2 - 12
examples/js/objects/Reflector.js

@@ -172,19 +172,9 @@ THREE.Reflector = function ( geometry, options ) {
 
 		// Restore viewport
 
-		var bounds = camera.bounds;
+		if ( camera.isArrayCamera ) {
 
-		if ( bounds !== undefined ) {
-
-			renderer.getSize( size );
-			var pixelRatio = renderer.getPixelRatio();
-
-			viewport.x = bounds.x * size.width * pixelRatio;
-			viewport.y = bounds.y * size.height * pixelRatio;
-			viewport.z = bounds.z * size.width * pixelRatio;
-			viewport.w = bounds.w * size.height * pixelRatio;
-
-			renderer.state.viewport( viewport );
+			renderer.state.viewport( camera.viewport );
 
 		}
 

+ 2 - 12
examples/js/objects/Refractor.js

@@ -210,19 +210,9 @@ THREE.Refractor = function ( geometry, options ) {
 
 			// restore viewport
 
-			var bounds = camera.bounds;
+			if ( camera.isArrayCamera ) {
 
-			if ( bounds !== undefined ) {
-
-				renderer.getSize( size );
-				var pixelRatio = renderer.getPixelRatio();
-
-				viewport.x = bounds.x * size.width * pixelRatio;
-				viewport.y = bounds.y * size.height * pixelRatio;
-				viewport.z = bounds.z * size.width * pixelRatio;
-				viewport.w = bounds.w * size.height * pixelRatio;
-
-				renderer.state.viewport( viewport );
+				renderer.state.viewport( camera.viewport );
 
 			}
 

+ 6 - 6
examples/js/postprocessing/SSAOPass.js

@@ -197,7 +197,7 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.ssaoRenderTarget.texture;
 				this.copyMaterial.blending = THREE.NoBlending;
-				this.renderPass( renderer, this.copyMaterial, null );
+				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 				break;
 
@@ -205,7 +205,7 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
 				this.copyMaterial.blending = THREE.NoBlending;
-				this.renderPass( renderer, this.copyMaterial, null );
+				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 				break;
 
@@ -213,13 +213,13 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
 				this.copyMaterial.blending = THREE.NoBlending;
-				this.renderPass( renderer, this.copyMaterial, null );
+				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 				break;
 
 			case THREE.SSAOPass.OUTPUT.Depth:
 
-				this.renderPass( renderer, this.depthRenderMaterial, null );
+				this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
 
 				break;
 
@@ -227,7 +227,7 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
 				this.copyMaterial.blending = THREE.NoBlending;
-				this.renderPass( renderer, this.copyMaterial, null );
+				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 				break;
 
@@ -235,7 +235,7 @@ THREE.SSAOPass.prototype = Object.assign( Object.create( THREE.Pass.prototype ),
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.beautyRenderTarget.texture;
 				this.copyMaterial.blending = THREE.NoBlending;
-				this.renderPass( renderer, this.copyMaterial, null );
+				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.blurRenderTarget.texture;
 				this.copyMaterial.blending = THREE.CustomBlending;

+ 2 - 1
examples/js/renderers/CSS2DRenderer.js

@@ -82,7 +82,8 @@ THREE.CSS2DRenderer = function () {
 			element.style.MozTransform = style;
 			element.style.oTransform = style;
 			element.style.transform = style;
-			element.style.display = ( vector.z < - 1 || vector.z > 1 ) ? 'none' : '';
+
+			element.style.display = ( object.visible && vector.z >= - 1 && vector.z <= 1 ) ? '' : 'none';
 
 			var objectData = {
 				distanceToCameraSquared: getDistanceToSquared( camera, object )

+ 3 - 2
examples/js/utils/ShadowMapViewer.js

@@ -49,7 +49,7 @@ THREE.ShadowMapViewer = function ( light ) {
 	//HUD for shadow map
 	var shader = THREE.UnpackDepthRGBAShader;
 
-	var uniforms = new THREE.UniformsUtils.clone( shader.uniforms );
+	var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
 	var material = new THREE.ShaderMaterial( {
 		uniforms: uniforms,
 		vertexShader: shader.vertexShader,
@@ -95,7 +95,7 @@ THREE.ShadowMapViewer = function ( light ) {
 	}
 
 
-	function resetPosition () {
+	function resetPosition() {
 
 		scope.position.set( scope.position.x, scope.position.y );
 
@@ -173,6 +173,7 @@ THREE.ShadowMapViewer = function ( light ) {
 			 camera.updateProjectionMatrix();
 
 			 this.update();
+
 		}
 
 	};

+ 56 - 45
examples/jsm/controls/OrbitControls.d.ts

@@ -1,70 +1,81 @@
 import { Camera, MOUSE, Object3D, Vector3 } from '../../../src/Three';
 
 export class OrbitControls {
-  constructor(object: Camera, domElement?: HTMLElement);
+	constructor(object: Camera, domElement?: HTMLElement);
 
-  object: Camera;
-  domElement: HTMLElement | HTMLDocument;
+	object: Camera;
+	domElement: HTMLElement | HTMLDocument;
 
-  // API
-  enabled: boolean;
-  target: Vector3;
+	// API
+	enabled: boolean;
+	target: Vector3;
 
-  // deprecated
-  center: Vector3;
+	// deprecated
+	center: Vector3;
 
-  enableZoom: boolean;
-  zoomSpeed: number;
-  minDistance: number;
-  maxDistance: number;
-  enableRotate: boolean;
-  rotateSpeed: number;
-  enablePan: boolean;
-  keyPanSpeed: number;
-  autoRotate: boolean;
-  autoRotateSpeed: number;
-  minPolarAngle: number;
-  maxPolarAngle: number;
-  minAzimuthAngle: number;
-  maxAzimuthAngle: number;
-  enableKeys: boolean;
-  keys: {LEFT: number; UP: number; RIGHT: number; BOTTOM: number;};
-  mouseButtons: {ORBIT: MOUSE; ZOOM: MOUSE; PAN: MOUSE;};
-  enableDamping: boolean;
-  dampingFactor: number;
-  screenSpacePanning: boolean;
+	minDistance: number;
+	maxDistance: number;
 
+	minZoom: number;
+	maxZoom: number;
 
-  rotateLeft(angle?: number): void;
+	minPolarAngle: number;
+	maxPolarAngle: number;
 
-  rotateUp(angle?: number): void;
+	minAzimuthAngle: number;
+	maxAzimuthAngle: number;
 
-  panLeft(distance?: number): void;
+	enableDamping: boolean;
+	dampingFactor: number;
 
-  panUp(distance?: number): void;
+	enableZoom: boolean;
+	zoomSpeed: number;
 
-  pan(deltaX: number, deltaY: number): void;
+	enableRotate: boolean;
+	rotateSpeed: number;
 
-  dollyIn(dollyScale: number): void;
+	enablePan: boolean;
+	panSpeed: number;
+	screenSpacePanning: boolean;
+	keyPanSpeed: number;
 
-  dollyOut(dollyScale: number): void;
+	autoRotate: boolean;
+	autoRotateSpeed: number;
 
-  update(): void;
+	enableKeys: boolean;
+	keys: { LEFT: number; UP: number; RIGHT: number; BOTTOM: number; };
+	mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE; };
 
-  reset(): void;
+	rotateLeft(angle?: number): void;
 
-  dispose(): void;
+	rotateUp(angle?: number): void;
 
-  getPolarAngle(): number;
+	panLeft(distance?: number): void;
 
-  getAzimuthalAngle(): number;
+	panUp(distance?: number): void;
 
-  // EventDispatcher mixins
-  addEventListener(type: string, listener: (event: any) => void): void;
+	pan(deltaX: number, deltaY: number): void;
 
-  hasEventListener(type: string, listener: (event: any) => void): boolean;
+	dollyIn(dollyScale: number): void;
 
-  removeEventListener(type: string, listener: (event: any) => void): void;
+	dollyOut(dollyScale: number): void;
 
-  dispatchEvent(event: {type: string; target: any;}): void;
+	update(): void;
+
+	reset(): void;
+
+	dispose(): void;
+
+	getPolarAngle(): number;
+
+	getAzimuthalAngle(): number;
+
+	// EventDispatcher mixins
+	addEventListener(type: string, listener: (event: any) => void): void;
+
+	hasEventListener(type: string, listener: (event: any) => void): boolean;
+
+	removeEventListener(type: string, listener: (event: any) => void): void;
+
+	dispatchEvent(event: { type: string; target: any; }): void;
 }

+ 9 - 5
examples/jsm/exporters/ColladaExporter.js

@@ -424,7 +424,7 @@ ColladaExporter.prototype = {
 
 					(
 						type !== 'constant' ?
-						'<diffuse>' +
+							'<diffuse>' +
 
 						(
 							m.map ?
@@ -432,12 +432,12 @@ ColladaExporter.prototype = {
 								`<color sid="diffuse">${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color>`
 						) +
 						'</diffuse>'
-						: ''
+							: ''
 					) +
 
 					(
 						type === 'phong' ?
-						`<specular><color sid="specular">${ specular.r } ${ specular.g } ${ specular.b } 1</color></specular>` +
+							`<specular><color sid="specular">${ specular.r } ${ specular.g } ${ specular.b } 1</color></specular>` +
 
 						'<shininess>' +
 
@@ -448,7 +448,7 @@ ColladaExporter.prototype = {
 						) +
 
 						'</shininess>'
-						: ''
+							: ''
 					) +
 
 					`<reflective><color>${ diffuse.r } ${ diffuse.g } ${ diffuse.b } 1</color></reflective>` +
@@ -537,9 +537,13 @@ ColladaExporter.prototype = {
 				var mat = o.material || new MeshBasicMaterial();
 				var materials = Array.isArray( mat ) ? mat : [ mat ];
 				if ( geometry.groups.length > materials.length ) {
+
 					matidsArray = new Array( geometry.groups.length );
+
 				} else {
-					matidsArray = new Array( materials.length )
+
+					matidsArray = new Array( materials.length );
+
 				}
 				matids = matidsArray.fill()
 					.map( ( v, i ) => processMaterial( materials[ i % materials.length ] ) );

+ 32 - 10
examples/jsm/exporters/GLTFExporter.js

@@ -147,9 +147,23 @@ GLTFExporter.prototype = {
 
 		var cachedCanvas;
 
+		var uids = new Map();
+		var uid = 0;
+
 		/**
-		 * Compare two arrays
+		 * Assign and return a temporal unique id for an object
+		 * especially which doesn't have .uuid
+		 * @param  {Object} object
+		 * @return {Integer}
 		 */
+		function getUID( object ) {
+
+			if ( ! uids.has( object ) ) uids.set( object, uid ++ );
+
+			return uids.get( object );
+
+		}
+
 		/**
 		 * Compare two arrays
 		 * @param  {Array} array1 Array 1 to compare
@@ -1202,9 +1216,9 @@ GLTFExporter.prototype = {
 
 				}
 
-				if ( cachedData.attributes.has( attribute ) ) {
+				if ( cachedData.attributes.has( getUID( attribute ) ) ) {
 
-					attributes[ attributeName ] = cachedData.attributes.get( attribute );
+					attributes[ attributeName ] = cachedData.attributes.get( getUID( attribute ) );
 					continue;
 
 				}
@@ -1225,7 +1239,7 @@ GLTFExporter.prototype = {
 				if ( accessor !== null ) {
 
 					attributes[ attributeName ] = accessor;
-					cachedData.attributes.set( attribute, accessor );
+					cachedData.attributes.set( getUID( attribute ), accessor );
 
 				}
 
@@ -1291,9 +1305,9 @@ GLTFExporter.prototype = {
 
 						var baseAttribute = geometry.attributes[ attributeName ];
 
-						if ( cachedData.attributes.has( attribute ) ) {
+						if ( cachedData.attributes.has( getUID( attribute ) ) ) {
 
-							target[ gltfAttributeName ] = cachedData.attributes.get( attribute );
+							target[ gltfAttributeName ] = cachedData.attributes.get( getUID( attribute ) );
 							continue;
 
 						}
@@ -1313,7 +1327,7 @@ GLTFExporter.prototype = {
 						}
 
 						target[ gltfAttributeName ] = processAccessor( relativeAttribute, geometry );
-						cachedData.attributes.set( baseAttribute, target[ gltfAttributeName ] );
+						cachedData.attributes.set( getUID( baseAttribute ), target[ gltfAttributeName ] );
 
 					}
 
@@ -1382,14 +1396,22 @@ GLTFExporter.prototype = {
 
 				if ( geometry.index !== null ) {
 
-					if ( cachedData.attributes.has( geometry.index ) ) {
+					var cacheKey = getUID( geometry.index );
+
+					if ( groups[ i ].start !== undefined || groups[ i ].count !== undefined ) {
+
+						cacheKey += ':' + groups[ i ].start + ':' + groups[ i ].count;
+
+					}
+
+					if ( cachedData.attributes.has( cacheKey ) ) {
 
-						primitive.indices = cachedData.attributes.get( geometry.index );
+						primitive.indices = cachedData.attributes.get( cacheKey );
 
 					} else {
 
 						primitive.indices = processAccessor( geometry.index, geometry, groups[ i ].start, groups[ i ].count );
-						cachedData.attributes.set( geometry.index, primitive.indices );
+						cachedData.attributes.set( cacheKey, primitive.indices );
 
 					}
 

+ 37 - 32
examples/jsm/loaders/GLTFLoader.js

@@ -460,26 +460,26 @@ var GLTFLoader = ( function () {
 	 *
 	 * PR: https://github.com/KhronosGroup/glTF/pull/1163
 	 */
-	function GLTFMaterialsUnlitExtension( json ) {
+	function GLTFMaterialsUnlitExtension() {
 
 		this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
 
 	}
 
-	GLTFMaterialsUnlitExtension.prototype.getMaterialType = function ( material ) {
+	GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () {
 
 		return MeshBasicMaterial;
 
 	};
 
-	GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, material, parser ) {
+	GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, materialDef, parser ) {
 
 		var pending = [];
 
 		materialParams.color = new Color( 1.0, 1.0, 1.0 );
 		materialParams.opacity = 1.0;
 
-		var metallicRoughness = material.pbrMetallicRoughness;
+		var metallicRoughness = materialDef.pbrMetallicRoughness;
 
 		if ( metallicRoughness ) {
 
@@ -655,7 +655,7 @@ var GLTFLoader = ( function () {
 	 *
 	 * Specification:
 	 */
-	function GLTFTextureTransformExtension( json ) {
+	function GLTFTextureTransformExtension() {
 
 		this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
 
@@ -738,9 +738,9 @@ var GLTFLoader = ( function () {
 
 			},
 
-			extendParams: function ( params, material, parser ) {
+			extendParams: function ( materialParams, materialDef, parser ) {
 
-				var pbrSpecularGlossiness = material.extensions[ this.name ];
+				var pbrSpecularGlossiness = materialDef.extensions[ this.name ];
 
 				var shader = ShaderLib[ 'standard' ];
 
@@ -803,13 +803,13 @@ var GLTFLoader = ( function () {
 				uniforms.specularMap = { value: null };
 				uniforms.glossinessMap = { value: null };
 
-				params.vertexShader = shader.vertexShader;
-				params.fragmentShader = fragmentShader;
-				params.uniforms = uniforms;
-				params.defines = { 'STANDARD': '' };
+				materialParams.vertexShader = shader.vertexShader;
+				materialParams.fragmentShader = fragmentShader;
+				materialParams.uniforms = uniforms;
+				materialParams.defines = { 'STANDARD': '' };
 
-				params.color = new Color( 1.0, 1.0, 1.0 );
-				params.opacity = 1.0;
+				materialParams.color = new Color( 1.0, 1.0, 1.0 );
+				materialParams.opacity = 1.0;
 
 				var pending = [];
 
@@ -817,32 +817,32 @@ var GLTFLoader = ( function () {
 
 					var array = pbrSpecularGlossiness.diffuseFactor;
 
-					params.color.fromArray( array );
-					params.opacity = array[ 3 ];
+					materialParams.color.fromArray( array );
+					materialParams.opacity = array[ 3 ];
 
 				}
 
 				if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {
 
-					pending.push( parser.assignTexture( params, 'map', pbrSpecularGlossiness.diffuseTexture ) );
+					pending.push( parser.assignTexture( materialParams, 'map', pbrSpecularGlossiness.diffuseTexture ) );
 
 				}
 
-				params.emissive = new Color( 0.0, 0.0, 0.0 );
-				params.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
-				params.specular = new Color( 1.0, 1.0, 1.0 );
+				materialParams.emissive = new Color( 0.0, 0.0, 0.0 );
+				materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
+				materialParams.specular = new Color( 1.0, 1.0, 1.0 );
 
 				if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) {
 
-					params.specular.fromArray( pbrSpecularGlossiness.specularFactor );
+					materialParams.specular.fromArray( pbrSpecularGlossiness.specularFactor );
 
 				}
 
 				if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {
 
 					var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
-					pending.push( parser.assignTexture( params, 'glossinessMap', specGlossMapDef ) );
-					pending.push( parser.assignTexture( params, 'specularMap', specGlossMapDef ) );
+					pending.push( parser.assignTexture( materialParams, 'glossinessMap', specGlossMapDef ) );
+					pending.push( parser.assignTexture( materialParams, 'specularMap', specGlossMapDef ) );
 
 				}
 
@@ -885,6 +885,7 @@ var GLTFLoader = ( function () {
 				material.bumpScale = 1;
 
 				material.normalMap = params.normalMap === undefined ? null : params.normalMap;
+
 				if ( params.normalScale ) material.normalScale = params.normalScale;
 
 				material.displacementMap = null;
@@ -2208,15 +2209,19 @@ var GLTFLoader = ( function () {
 
 		return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) {
 
-			switch ( mapName ) {
+			if ( ! texture.isCompressedTexture ) {
 
-				case 'aoMap':
-				case 'emissiveMap':
-				case 'metalnessMap':
-				case 'normalMap':
-				case 'roughnessMap':
-					texture.format = RGBFormat;
-					break;
+				switch ( mapName ) {
+
+					case 'aoMap':
+					case 'emissiveMap':
+					case 'metalnessMap':
+					case 'normalMap':
+					case 'roughnessMap':
+						texture.format = RGBFormat;
+						break;
+
+				}
 
 			}
 
@@ -2377,13 +2382,13 @@ var GLTFLoader = ( function () {
 		if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) {
 
 			var sgExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ];
-			materialType = sgExtension.getMaterialType( materialDef );
+			materialType = sgExtension.getMaterialType();
 			pending.push( sgExtension.extendParams( materialParams, materialDef, parser ) );
 
 		} else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) {
 
 			var kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ];
-			materialType = kmuExtension.getMaterialType( materialDef );
+			materialType = kmuExtension.getMaterialType();
 			pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) );
 
 		} else {

+ 357 - 0
examples/jsm/loaders/STLLoader.js

@@ -0,0 +1,357 @@
+/**
+ * @author aleeper / http://adamleeper.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author gero3 / https://github.com/gero3
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs.
+ *
+ * Supports both binary and ASCII encoded files, with automatic detection of type.
+ *
+ * The loader returns a non-indexed buffer geometry.
+ *
+ * Limitations:
+ *  Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL).
+ *  There is perhaps some question as to how valid it is to always assume little-endian-ness.
+ *  ASCII decoding assumes file is UTF-8.
+ *
+ * Usage:
+ *  var loader = new STLLoader();
+ *  loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
+ *    scene.add( new Mesh( geometry ) );
+ *  });
+ *
+ * For binary STLs geometry might contain colors for vertices. To use it:
+ *  // use the same code to load STL as above
+ *  if (geometry.hasColors) {
+ *    material = new MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: VertexColors });
+ *  } else { .... }
+ *  var mesh = new Mesh( geometry, material );
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	LoaderUtils,
+	Mesh,
+	MeshPhongMaterial,
+	Vector3,
+	VertexColors
+} from "../../../build/three.module.js";
+
+
+var STLLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+};
+
+STLLoader.prototype = {
+
+	constructor: STLLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( text ) {
+
+			try {
+
+				onLoad( scope.parse( text ) );
+
+			} catch ( exception ) {
+
+				if ( onError ) {
+
+					onError( exception );
+
+				}
+
+			}
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( data ) {
+
+		function isBinary( data ) {
+
+			var expect, face_size, n_faces, reader;
+			reader = new DataView( data );
+			face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 );
+			n_faces = reader.getUint32( 80, true );
+			expect = 80 + ( 32 / 8 ) + ( n_faces * face_size );
+
+			if ( expect === reader.byteLength ) {
+
+				return true;
+
+			}
+
+			// An ASCII STL data must begin with 'solid ' as the first six bytes.
+			// However, ASCII STLs lacking the SPACE after the 'd' are known to be
+			// plentiful.  So, check the first 5 bytes for 'solid'.
+
+			// Several encodings, such as UTF-8, precede the text with up to 5 bytes:
+			// https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding
+			// Search for "solid" to start anywhere after those prefixes.
+
+			// US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd'
+
+			var solid = [ 115, 111, 108, 105, 100 ];
+
+			for ( var off = 0; off < 5; off ++ ) {
+
+				// If "solid" text is matched to the current offset, declare it to be an ASCII STL.
+
+				if ( matchDataViewAt ( solid, reader, off ) ) return false;
+
+			}
+
+			// Couldn't find "solid" text at the beginning; it is binary STL.
+
+			return true;
+
+		}
+
+		function matchDataViewAt( query, reader, offset ) {
+
+			// Check if each byte in query matches the corresponding byte from the current offset
+
+			for ( var i = 0, il = query.length; i < il; i ++ ) {
+
+				if ( query[ i ] !== reader.getUint8( offset + i, false ) ) return false;
+
+			}
+
+			return true;
+
+		}
+
+		function parseBinary( data ) {
+
+			var reader = new DataView( data );
+			var faces = reader.getUint32( 80, true );
+
+			var r, g, b, hasColors = false, colors;
+			var defaultR, defaultG, defaultB, alpha;
+
+			// process STL header
+			// check for default color in header ("COLOR=rgba" sequence).
+
+			for ( var index = 0; index < 80 - 10; index ++ ) {
+
+				if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) &&
+					( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) &&
+					( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) {
+
+					hasColors = true;
+					colors = [];
+
+					defaultR = reader.getUint8( index + 6 ) / 255;
+					defaultG = reader.getUint8( index + 7 ) / 255;
+					defaultB = reader.getUint8( index + 8 ) / 255;
+					alpha = reader.getUint8( index + 9 ) / 255;
+
+				}
+
+			}
+
+			var dataOffset = 84;
+			var faceLength = 12 * 4 + 2;
+
+			var geometry = new BufferGeometry();
+
+			var vertices = [];
+			var normals = [];
+
+			for ( var face = 0; face < faces; face ++ ) {
+
+				var start = dataOffset + face * faceLength;
+				var normalX = reader.getFloat32( start, true );
+				var normalY = reader.getFloat32( start + 4, true );
+				var normalZ = reader.getFloat32( start + 8, true );
+
+				if ( hasColors ) {
+
+					var packedColor = reader.getUint16( start + 48, true );
+
+					if ( ( packedColor & 0x8000 ) === 0 ) {
+
+						// facet has its own unique color
+
+						r = ( packedColor & 0x1F ) / 31;
+						g = ( ( packedColor >> 5 ) & 0x1F ) / 31;
+						b = ( ( packedColor >> 10 ) & 0x1F ) / 31;
+
+					} else {
+
+						r = defaultR;
+						g = defaultG;
+						b = defaultB;
+
+					}
+
+				}
+
+				for ( var i = 1; i <= 3; i ++ ) {
+
+					var vertexstart = start + i * 12;
+
+					vertices.push( reader.getFloat32( vertexstart, true ) );
+					vertices.push( reader.getFloat32( vertexstart + 4, true ) );
+					vertices.push( reader.getFloat32( vertexstart + 8, true ) );
+
+					normals.push( normalX, normalY, normalZ );
+
+					if ( hasColors ) {
+
+						colors.push( r, g, b );
+
+					}
+
+				}
+
+			}
+
+			geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( vertices ), 3 ) );
+			geometry.addAttribute( 'normal', new BufferAttribute( new Float32Array( normals ), 3 ) );
+
+			if ( hasColors ) {
+
+				geometry.addAttribute( 'color', new BufferAttribute( new Float32Array( colors ), 3 ) );
+				geometry.hasColors = true;
+				geometry.alpha = alpha;
+
+			}
+
+			return geometry;
+
+		}
+
+		function parseASCII( data ) {
+
+			var geometry = new BufferGeometry();
+			var patternFace = /facet([\s\S]*?)endfacet/g;
+			var faceCounter = 0;
+
+			var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source;
+			var patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' );
+			var patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' );
+
+			var vertices = [];
+			var normals = [];
+
+			var normal = new Vector3();
+
+			var result;
+
+			while ( ( result = patternFace.exec( data ) ) !== null ) {
+
+				var vertexCountPerFace = 0;
+				var normalCountPerFace = 0;
+
+				var text = result[ 0 ];
+
+				while ( ( result = patternNormal.exec( text ) ) !== null ) {
+
+					normal.x = parseFloat( result[ 1 ] );
+					normal.y = parseFloat( result[ 2 ] );
+					normal.z = parseFloat( result[ 3 ] );
+					normalCountPerFace ++;
+
+				}
+
+				while ( ( result = patternVertex.exec( text ) ) !== null ) {
+
+					vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
+					normals.push( normal.x, normal.y, normal.z );
+					vertexCountPerFace ++;
+
+				}
+
+				// every face have to own ONE valid normal
+
+				if ( normalCountPerFace !== 1 ) {
+
+					console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
+
+				}
+
+				// each face have to own THREE valid vertices
+
+				if ( vertexCountPerFace !== 3 ) {
+
+					console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
+
+				}
+
+				faceCounter ++;
+
+			}
+
+			geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+			geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+
+			return geometry;
+
+		}
+
+		function ensureString( buffer ) {
+
+			if ( typeof buffer !== 'string' ) {
+
+				return LoaderUtils.decodeText( new Uint8Array( buffer ) );
+
+			}
+
+			return buffer;
+
+		}
+
+		function ensureBinary( buffer ) {
+
+			if ( typeof buffer === 'string' ) {
+
+				var array_buffer = new Uint8Array( buffer.length );
+				for ( var i = 0; i < buffer.length; i ++ ) {
+
+					array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
+
+				}
+				return array_buffer.buffer || array_buffer;
+
+			} else {
+
+				return buffer;
+
+			}
+
+		}
+
+		// start
+
+		var binData = ensureBinary( data );
+
+		return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) );
+
+	}
+
+};
+
+export { STLLoader };

+ 32 - 40
examples/jsm/utils/MathUtils.js

@@ -7,69 +7,61 @@
 
 var MathUtils = {
 
+    setQuaternionFromProperEuler: function ( q, a, b, c, order ) {
 
-	/**
-	 * @param {Quaternion} q
-	 * @param {number} a
-	 * @param {number} b
-	 * @param {number} c
-	 * @param {string} order
-	 */
-	setQuaternionFromProperEuler: function (q, a, b, c, order) {
+        // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
 
-		// Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
+        // rotations are applied to the axes in the order specified by 'order'
+        // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
+        // angles are in radians
 
-		// rotations are applied to the axes in the order specified by 'order'
-		// rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
-		// angles are in radians
+        var cos = Math.cos;
+        var sin = Math.sin;
 
-		var cos = Math.cos;
-		var sin = Math.sin;
+        var c2 = cos( b / 2 );
+        var s2 = sin( b / 2 );
 
-		var c2 = cos(b / 2);
-		var s2 = sin(b / 2);
+        var c13 = cos( ( a + c ) / 2 );
+        var s13 = sin( ( a + c ) / 2 );
 
-		var c13 = cos((a + c) / 2);
-		var s13 = sin((a + c) / 2);
+        var c1_3 = cos( ( a - c ) / 2 );
+        var s1_3 = sin( ( a - c ) / 2 );
 
-		var c1_3 = cos((a - c) / 2);
-		var s1_3 = sin((a - c) / 2);
+        var c3_1 = cos( ( c - a ) / 2 );
+        var s3_1 = sin( ( c - a ) / 2 );
 
-		var c3_1 = cos((c - a) / 2);
-		var s3_1 = sin((c - a) / 2);
+        if ( order === 'XYX' ) {
 
-		if (order === 'XYX') {
+            q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );
 
-			q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13);
+        } else if ( order === 'YZY' ) {
 
-		} else if (order === 'YZY') {
+            q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );
 
-			q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13);
+        } else if ( order === 'ZXZ' ) {
 
-		} else if (order === 'ZXZ') {
+            q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );
 
-			q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13);
+        } else if ( order === 'XZX' ) {
 
-		} else if (order === 'XZX') {
+            q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );
 
-			q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13);
+        } else if ( order === 'YXY' ) {
 
-		} else if (order === 'YXY') {
+            q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );
 
-			q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13);
+        } else if ( order === 'ZYZ' ) {
 
-		} else if (order === 'ZYZ') {
+            q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );
 
-			q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13);
+        } else {
 
-		} else {
+            console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order.' );
 
-			console.warn('THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order.');
+        }
 
-		}
-
-	}
+    }
 
 };
 
-export {MathUtils};
+export { MathUtils };

Some files were not shown because too many files changed in this diff