Pārlūkot izejas kodu

Merge remote-tracking branch 'upstream/dev' into NewVAOSupport

Takahiro 5 gadi atpakaļ
vecāks
revīzija
2c7e37bf8f
43 mainītis faili ar 1318 papildinājumiem un 806 dzēšanām
  1. 0 7
      .npmignore
  2. 131 114
      build/three.js
  3. 380 379
      build/three.min.js
  4. 131 114
      build/three.module.js
  5. 1 1
      docs/api/en/math/Box3.html
  6. 8 0
      docs/api/en/math/Color.html
  7. 94 0
      docs/api/en/textures/DataTexture2DArray.html
  8. 8 0
      docs/api/zh/math/Color.html
  9. 94 0
      docs/api/zh/textures/DataTexture2DArray.html
  10. 10 0
      docs/examples/en/controls/PointerLockControls.html
  11. 10 0
      docs/examples/zh/controls/PointerLockControls.html
  12. 2 0
      docs/list.js
  13. 14 21
      editor/css/main.css
  14. 16 0
      editor/images/rotate.svg
  15. 60 0
      editor/images/scale.svg
  16. 46 0
      editor/images/translate.svg
  17. 1 1
      editor/js/Loader.js
  18. 12 0
      editor/js/Menubar.Help.js
  19. 3 0
      editor/js/Strings.js
  20. 25 20
      editor/js/Toolbar.js
  21. 4 1
      editor/sw.js
  22. 6 1
      examples/js/controls/PointerLockControls.js
  23. 6 4
      examples/js/controls/TransformControls.js
  24. 14 0
      examples/js/loaders/EXRLoader.js
  25. 17 3
      examples/js/renderers/CSS2DRenderer.js
  26. 16 3
      examples/js/renderers/CSS3DRenderer.js
  27. 3 0
      examples/jsm/controls/PointerLockControls.d.ts
  28. 6 1
      examples/jsm/controls/PointerLockControls.js
  29. 6 4
      examples/jsm/controls/TransformControls.js
  30. 1 1
      examples/jsm/libs/stats.module.d.ts
  31. 14 0
      examples/jsm/loaders/EXRLoader.js
  32. 17 3
      examples/jsm/renderers/CSS2DRenderer.js
  33. 16 3
      examples/jsm/renderers/CSS3DRenderer.js
  34. 13 13
      package-lock.json
  35. 2 2
      package.json
  36. 32 31
      src/core/Geometry.js
  37. 1 1
      src/materials/Material.js
  38. 4 0
      src/math/Color.d.ts
  39. 20 0
      src/math/Color.js
  40. 55 66
      src/renderers/WebGLRenderer.js
  41. 1 1
      src/renderers/webgl/WebGLBackground.js
  42. 16 10
      src/renderers/webgl/WebGLPrograms.js
  43. 2 1
      test/package.json

+ 0 - 7
.npmignore

@@ -1,7 +0,0 @@
-examples/*
-!examples/js/
-test/
-utils/
-docs/
-editor/
-.DS_Store

+ 131 - 114
build/three.js

@@ -8617,6 +8617,26 @@
 
 		},
 
+		fromBufferAttribute: function ( attribute, index ) {
+
+			this.r = attribute.getX( index );
+			this.g = attribute.getY( index );
+			this.b = attribute.getZ( index );
+
+			if ( attribute.normalized === true ) {
+
+				// assuming Uint8Array
+
+				this.r /= 255;
+				this.g /= 255;
+				this.b /= 255;
+
+			}
+
+			return this;
+
+		},
+
 		toJSON: function () {
 
 			return this.getHex();
@@ -8766,7 +8786,7 @@
 
 		isMaterial: true,
 
-		onBeforeCompile: function () {},
+		onBeforeCompile: function ( /* shaderobject, renderer */ ) {},
 
 		setValues: function ( values ) {
 
@@ -11882,7 +11902,7 @@
 
 			var scope = this;
 
-			var indices = geometry.index !== null ? geometry.index.array : undefined;
+			var index = geometry.index !== null ? geometry.index : undefined;
 			var attributes = geometry.attributes;
 
 			if ( attributes.position === undefined ) {
@@ -11892,21 +11912,21 @@
 
 			}
 
-			var positions = attributes.position.array;
-			var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
-			var colors = attributes.color !== undefined ? attributes.color.array : undefined;
-			var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
-			var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
+			var position = attributes.position;
+			var normal = attributes.normal;
+			var color = attributes.color;
+			var uv = attributes.uv;
+			var uv2 = attributes.uv2;
 
-			if ( uvs2 !== undefined ) { this.faceVertexUvs[ 1 ] = []; }
+			if ( uv2 !== undefined ) { this.faceVertexUvs[ 1 ] = []; }
 
-			for ( var i = 0; i < positions.length; i += 3 ) {
+			for ( var i = 0; i < position.count; i ++ ) {
 
-				scope.vertices.push( new Vector3().fromArray( positions, i ) );
+				scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) );
 
-				if ( colors !== undefined ) {
+				if ( color !== undefined ) {
 
-					scope.colors.push( new Color().fromArray( colors, i ) );
+					scope.colors.push( new Color().fromBufferAttribute( color, i ) );
 
 				}
 
@@ -11914,37 +11934,38 @@
 
 			function addFace( a, b, c, materialIndex ) {
 
-				var vertexColors = ( colors === undefined ) ? [] : [
+				var vertexColors = ( color === undefined ) ? [] : [
 					scope.colors[ a ].clone(),
 					scope.colors[ b ].clone(),
-					scope.colors[ c ].clone() ];
+					scope.colors[ c ].clone()
+				];
 
-				var vertexNormals = ( normals === undefined ) ? [] : [
-					new Vector3().fromArray( normals, a * 3 ),
-					new Vector3().fromArray( normals, b * 3 ),
-					new Vector3().fromArray( normals, c * 3 )
+				var vertexNormals = ( normal === undefined ) ? [] : [
+					new Vector3().fromBufferAttribute( normal, a ),
+					new Vector3().fromBufferAttribute( normal, b ),
+					new Vector3().fromBufferAttribute( normal, c )
 				];
 
 				var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );
 
 				scope.faces.push( face );
 
-				if ( uvs !== undefined ) {
+				if ( uv !== undefined ) {
 
 					scope.faceVertexUvs[ 0 ].push( [
-						new Vector2().fromArray( uvs, a * 2 ),
-						new Vector2().fromArray( uvs, b * 2 ),
-						new Vector2().fromArray( uvs, c * 2 )
+						new Vector2().fromBufferAttribute( uv, a ),
+						new Vector2().fromBufferAttribute( uv, b ),
+						new Vector2().fromBufferAttribute( uv, c )
 					] );
 
 				}
 
-				if ( uvs2 !== undefined ) {
+				if ( uv2 !== undefined ) {
 
 					scope.faceVertexUvs[ 1 ].push( [
-						new Vector2().fromArray( uvs2, a * 2 ),
-						new Vector2().fromArray( uvs2, b * 2 ),
-						new Vector2().fromArray( uvs2, c * 2 )
+						new Vector2().fromBufferAttribute( uv2, a ),
+						new Vector2().fromBufferAttribute( uv2, b ),
+						new Vector2().fromBufferAttribute( uv2, c )
 					] );
 
 				}
@@ -11964,9 +11985,9 @@
 
 					for ( var j = start, jl = start + count; j < jl; j += 3 ) {
 
-						if ( indices !== undefined ) {
+						if ( index !== undefined ) {
 
-							addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );
+							addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex );
 
 						} else {
 
@@ -11980,17 +12001,17 @@
 
 			} else {
 
-				if ( indices !== undefined ) {
+				if ( index !== undefined ) {
 
-					for ( var i$2 = 0; i$2 < indices.length; i$2 += 3 ) {
+					for ( var i$2 = 0; i$2 < index.count; i$2 += 3 ) {
 
-						addFace( indices[ i$2 ], indices[ i$2 + 1 ], indices[ i$2 + 2 ] );
+						addFace( index.getX( i$2 ), index.getX( i$2 + 1 ), index.getX( i$2 + 2 ) );
 
 					}
 
 				} else {
 
-					for ( var i$3 = 0; i$3 < positions.length / 3; i$3 += 3 ) {
+					for ( var i$3 = 0; i$3 < position.count; i$3 += 3 ) {
 
 						addFace( i$3, i$3 + 1, i$3 + 2 );
 
@@ -15541,7 +15562,7 @@
 
 		function render( renderList, scene, camera, forceClear ) {
 
-			var background = scene.background;
+			var background = scene.isScene === true ? scene.background : null;
 
 			// Ignore background in AR
 			// TODO: Reconsider this.
@@ -18671,7 +18692,7 @@
 
 		}
 
-		this.getParameters = function ( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
+		function getParameters( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
 
 			var fog = scene.fog;
 			var environment = material.isMeshStandardMaterial ? scene.environment : null;
@@ -18824,9 +18845,9 @@
 
 			return parameters;
 
-		};
+		}
 
-		this.getProgramCacheKey = function ( parameters ) {
+		function getProgramCacheKey( parameters ) {
 
 			var array = [];
 
@@ -18869,9 +18890,9 @@
 
 			return array.join();
 
-		};
+		}
 
-		this.acquireProgram = function ( parameters, cacheKey ) {
+		function acquireProgram( parameters, cacheKey ) {
 
 			var program;
 
@@ -18900,9 +18921,9 @@
 
 			return program;
 
-		};
+		}
 
-		this.releaseProgram = function ( program ) {
+		function releaseProgram( program ) {
 
 			if ( -- program.usedTimes === 0 ) {
 
@@ -18916,10 +18937,16 @@
 
 			}
 
-		};
+		}
 
-		// Exposed for resource monitoring & error feedback via renderer.info:
-		this.programs = programs;
+		return {
+			getParameters: getParameters,
+			getProgramCacheKey: getProgramCacheKey,
+			acquireProgram: acquireProgram,
+			releaseProgram: releaseProgram,
+			// Exposed for resource monitoring & error feedback via renderer.info:
+			programs: programs
+		};
 
 	}
 
@@ -24265,63 +24292,65 @@
 
 		// internal properties
 
-		var _this = this,
+		var _this = this;
 
-			_isContextLost = false,
+		var _isContextLost = false;
 
-			// internal state cache
+		// internal state cache
 
-			_framebuffer = null,
+		var _framebuffer = null;
 
-			_currentActiveCubeFace = 0,
-			_currentActiveMipmapLevel = 0,
-			_currentRenderTarget = null,
-			_currentFramebuffer = null,
-			_currentMaterialId = - 1,
+		var _currentActiveCubeFace = 0;
+		var _currentActiveMipmapLevel = 0;
+		var _currentRenderTarget = null;
+		var _currentFramebuffer = null;
+		var _currentMaterialId = - 1;
 
-			// geometry and program caching
+		// geometry and program caching
 
-			_currentGeometryProgram = {
-				geometry: null,
-				program: null,
-				wireframe: false
-			},
+		var _currentGeometryProgram = {
+			geometry: null,
+			program: null,
+			wireframe: false
+		};
 
-			_currentCamera = null,
-			_currentArrayCamera = null,
+		var _currentCamera = null;
+		var _currentArrayCamera = null;
 
-			_currentViewport = new Vector4(),
-			_currentScissor = new Vector4(),
-			_currentScissorTest = null,
+		var _currentViewport = new Vector4();
+		var _currentScissor = new Vector4();
+		var _currentScissorTest = null;
 
-			//
+		//
 
-			_width = _canvas.width,
-			_height = _canvas.height,
+		var _width = _canvas.width;
+		var _height = _canvas.height;
 
-			_pixelRatio = 1,
-			_opaqueSort = null,
-			_transparentSort = null,
+		var _pixelRatio = 1;
+		var _opaqueSort = null;
+		var _transparentSort = null;
 
-			_viewport = new Vector4( 0, 0, _width, _height ),
-			_scissor = new Vector4( 0, 0, _width, _height ),
-			_scissorTest = false,
+		var _viewport = new Vector4( 0, 0, _width, _height );
+		var _scissor = new Vector4( 0, 0, _width, _height );
+		var _scissorTest = false;
 
-			// frustum
+		// frustum
 
-			_frustum = new Frustum(),
+		var _frustum = new Frustum();
+
+		// clipping
 
-			// clipping
+		var _clipping = new WebGLClipping();
+		var _clippingEnabled = false;
+		var _localClippingEnabled = false;
 
-			_clipping = new WebGLClipping(),
-			_clippingEnabled = false,
-			_localClippingEnabled = false,
+		// camera matrices cache
 
-			// camera matrices cache
+		var _projScreenMatrix = new Matrix4();
 
-			_projScreenMatrix = new Matrix4(),
+		var _vector3 = new Vector3();
 
-			_vector3 = new Vector3();
+		var _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
 
 		function getTargetPixelRatio() {
 
@@ -24860,11 +24889,9 @@
 
 		};
 
-		var tempScene = new Scene();
-
 		this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
 
-			if ( scene === null ) { scene = tempScene; } // renderBufferDirect second parameter used to be fog (could be null)
+			if ( scene === null ) { scene = _emptyScene; } // renderBufferDirect second parameter used to be fog (could be null)
 
 			var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
@@ -25286,14 +25313,14 @@
 
 			}
 
-			if ( ! ( camera && camera.isCamera ) ) {
+			if ( camera !== undefined && camera.isCamera !== true ) {
 
 				console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
 				return;
 
 			}
 
-			if ( _isContextLost ) { return; }
+			if ( _isContextLost === true ) { return; }
 
 			// reset caching for this frame
 
@@ -25311,14 +25338,14 @@
 
 			if ( camera.parent === null ) { camera.updateMatrixWorld(); }
 
-			if ( xr.enabled && xr.isPresenting ) {
+			if ( xr.enabled === true && xr.isPresenting === true ) {
 
 				camera = xr.getCamera( camera );
 
 			}
 
 			//
-			if ( scene.isScene ) { scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); }
+			if ( scene.isScene === true ) { scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); }
 
 			currentRenderState = renderStates.get( scene, camera );
 			currentRenderState.init();
@@ -25344,7 +25371,7 @@
 
 			//
 
-			if ( _clippingEnabled ) { _clipping.beginShadows(); }
+			if ( _clippingEnabled === true ) { _clipping.beginShadows(); }
 
 			var shadowsArray = currentRenderState.state.shadowsArray;
 
@@ -25352,11 +25379,11 @@
 
 			currentRenderState.setupLights( camera );
 
-			if ( _clippingEnabled ) { _clipping.endShadows(); }
+			if ( _clippingEnabled === true ) { _clipping.endShadows(); }
 
 			//
 
-			if ( this.info.autoReset ) { this.info.reset(); }
+			if ( this.info.autoReset === true ) { this.info.reset(); }
 
 			if ( renderTarget !== undefined ) {
 
@@ -25373,28 +25400,12 @@
 			var opaqueObjects = currentRenderList.opaque;
 			var transparentObjects = currentRenderList.transparent;
 
-			if ( scene.overrideMaterial ) {
-
-				var overrideMaterial = scene.overrideMaterial;
-
-				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera, overrideMaterial ); }
-				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera, overrideMaterial ); }
-
-			} else {
-
-				// opaque pass (front-to-back order)
-
-				if ( opaqueObjects.length ) { renderObjects( opaqueObjects, scene, camera ); }
-
-				// transparent pass (back-to-front order)
-
-				if ( transparentObjects.length ) { renderObjects( transparentObjects, scene, camera ); }
-
-			}
+			if ( opaqueObjects.length > 0 ) { renderObjects( opaqueObjects, scene, camera ); }
+			if ( transparentObjects.length > 0 ) { renderObjects( transparentObjects, scene, camera ); }
 
 			//
 
-			if ( scene.isScene ) { scene.onAfterRender( _this, scene, camera ); }
+			if ( scene.isScene === true ) { scene.onAfterRender( _this, scene, camera ); }
 
 			//
 
@@ -25550,7 +25561,9 @@
 
 		}
 
-		function renderObjects( renderList, scene, camera, overrideMaterial ) {
+		function renderObjects( renderList, scene, camera ) {
+
+			var overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
 
 			for ( var i = 0, l = renderList.length; i < l; i ++ ) {
 
@@ -25558,7 +25571,7 @@
 
 				var object = renderItem.object;
 				var geometry = renderItem.geometry;
-				var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+				var material = overrideMaterial === null ? renderItem.material : overrideMaterial;
 				var group = renderItem.group;
 
 				if ( camera.isArrayCamera ) {
@@ -25628,6 +25641,8 @@
 
 		function initMaterial( material, scene, object ) {
 
+			if ( scene.isScene !== true ) { scene = _emptyScene; } // scene could be a Mesh, Line, Points, ...
+
 			var materialProperties = properties.get( material );
 
 			var lights = currentRenderState.state.lights;
@@ -25769,6 +25784,8 @@
 
 		function setProgram( camera, scene, material, object ) {
 
+			if ( scene.isScene !== true ) { scene = _emptyScene; } // scene could be a Mesh, Line, Points, ...
+
 			textures.resetTextureUnits();
 
 			var fog = scene.fog;
@@ -25778,9 +25795,9 @@
 			var materialProperties = properties.get( material );
 			var lights = currentRenderState.state.lights;
 
-			if ( _clippingEnabled ) {
+			if ( _clippingEnabled === true ) {
 
-				if ( _localClippingEnabled || camera !== _currentCamera ) {
+				if ( _localClippingEnabled === true || camera !== _currentCamera ) {
 
 					var useCache =
 						camera === _currentCamera &&

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 380 - 379
build/three.min.js


+ 131 - 114
build/three.module.js

@@ -8611,6 +8611,26 @@ Object.assign( Color.prototype, {
 
 	},
 
+	fromBufferAttribute: function ( attribute, index ) {
+
+		this.r = attribute.getX( index );
+		this.g = attribute.getY( index );
+		this.b = attribute.getZ( index );
+
+		if ( attribute.normalized === true ) {
+
+			// assuming Uint8Array
+
+			this.r /= 255;
+			this.g /= 255;
+			this.b /= 255;
+
+		}
+
+		return this;
+
+	},
+
 	toJSON: function () {
 
 		return this.getHex();
@@ -8760,7 +8780,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 	isMaterial: true,
 
-	onBeforeCompile: function () {},
+	onBeforeCompile: function ( /* shaderobject, renderer */ ) {},
 
 	setValues: function ( values ) {
 
@@ -11876,7 +11896,7 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		const scope = this;
 
-		const indices = geometry.index !== null ? geometry.index.array : undefined;
+		const index = geometry.index !== null ? geometry.index : undefined;
 		const attributes = geometry.attributes;
 
 		if ( attributes.position === undefined ) {
@@ -11886,21 +11906,21 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		}
 
-		const positions = attributes.position.array;
-		const normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
-		const colors = attributes.color !== undefined ? attributes.color.array : undefined;
-		const uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
-		const uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
+		const position = attributes.position;
+		const normal = attributes.normal;
+		const color = attributes.color;
+		const uv = attributes.uv;
+		const uv2 = attributes.uv2;
 
-		if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
+		if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
 
-		for ( let i = 0; i < positions.length; i += 3 ) {
+		for ( let i = 0; i < position.count; i ++ ) {
 
-			scope.vertices.push( new Vector3().fromArray( positions, i ) );
+			scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) );
 
-			if ( colors !== undefined ) {
+			if ( color !== undefined ) {
 
-				scope.colors.push( new Color().fromArray( colors, i ) );
+				scope.colors.push( new Color().fromBufferAttribute( color, i ) );
 
 			}
 
@@ -11908,37 +11928,38 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		function addFace( a, b, c, materialIndex ) {
 
-			const vertexColors = ( colors === undefined ) ? [] : [
+			const vertexColors = ( color === undefined ) ? [] : [
 				scope.colors[ a ].clone(),
 				scope.colors[ b ].clone(),
-				scope.colors[ c ].clone() ];
+				scope.colors[ c ].clone()
+			];
 
-			const vertexNormals = ( normals === undefined ) ? [] : [
-				new Vector3().fromArray( normals, a * 3 ),
-				new Vector3().fromArray( normals, b * 3 ),
-				new Vector3().fromArray( normals, c * 3 )
+			const vertexNormals = ( normal === undefined ) ? [] : [
+				new Vector3().fromBufferAttribute( normal, a ),
+				new Vector3().fromBufferAttribute( normal, b ),
+				new Vector3().fromBufferAttribute( normal, c )
 			];
 
 			const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );
 
 			scope.faces.push( face );
 
-			if ( uvs !== undefined ) {
+			if ( uv !== undefined ) {
 
 				scope.faceVertexUvs[ 0 ].push( [
-					new Vector2().fromArray( uvs, a * 2 ),
-					new Vector2().fromArray( uvs, b * 2 ),
-					new Vector2().fromArray( uvs, c * 2 )
+					new Vector2().fromBufferAttribute( uv, a ),
+					new Vector2().fromBufferAttribute( uv, b ),
+					new Vector2().fromBufferAttribute( uv, c )
 				] );
 
 			}
 
-			if ( uvs2 !== undefined ) {
+			if ( uv2 !== undefined ) {
 
 				scope.faceVertexUvs[ 1 ].push( [
-					new Vector2().fromArray( uvs2, a * 2 ),
-					new Vector2().fromArray( uvs2, b * 2 ),
-					new Vector2().fromArray( uvs2, c * 2 )
+					new Vector2().fromBufferAttribute( uv2, a ),
+					new Vector2().fromBufferAttribute( uv2, b ),
+					new Vector2().fromBufferAttribute( uv2, c )
 				] );
 
 			}
@@ -11958,9 +11979,9 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 				for ( let j = start, jl = start + count; j < jl; j += 3 ) {
 
-					if ( indices !== undefined ) {
+					if ( index !== undefined ) {
 
-						addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );
+						addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex );
 
 					} else {
 
@@ -11974,17 +11995,17 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		} else {
 
-			if ( indices !== undefined ) {
+			if ( index !== undefined ) {
 
-				for ( let i = 0; i < indices.length; i += 3 ) {
+				for ( let i = 0; i < index.count; i += 3 ) {
 
-					addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+					addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) );
 
 				}
 
 			} else {
 
-				for ( let i = 0; i < positions.length / 3; i += 3 ) {
+				for ( let i = 0; i < position.count; i += 3 ) {
 
 					addFace( i, i + 1, i + 2 );
 
@@ -15528,7 +15549,7 @@ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
 
 	function render( renderList, scene, camera, forceClear ) {
 
-		let background = scene.background;
+		let background = scene.isScene === true ? scene.background : null;
 
 		// Ignore background in AR
 		// TODO: Reconsider this.
@@ -18658,7 +18679,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 	}
 
-	this.getParameters = function ( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
+	function getParameters( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
 
 		const fog = scene.fog;
 		const environment = material.isMeshStandardMaterial ? scene.environment : null;
@@ -18811,9 +18832,9 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		return parameters;
 
-	};
+	}
 
-	this.getProgramCacheKey = function ( parameters ) {
+	function getProgramCacheKey( parameters ) {
 
 		const array = [];
 
@@ -18856,9 +18877,9 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		return array.join();
 
-	};
+	}
 
-	this.acquireProgram = function ( parameters, cacheKey ) {
+	function acquireProgram( parameters, cacheKey ) {
 
 		let program;
 
@@ -18887,9 +18908,9 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		return program;
 
-	};
+	}
 
-	this.releaseProgram = function ( program ) {
+	function releaseProgram( program ) {
 
 		if ( -- program.usedTimes === 0 ) {
 
@@ -18903,10 +18924,16 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		}
 
-	};
+	}
 
-	// Exposed for resource monitoring & error feedback via renderer.info:
-	this.programs = programs;
+	return {
+		getParameters: getParameters,
+		getProgramCacheKey: getProgramCacheKey,
+		acquireProgram: acquireProgram,
+		releaseProgram: releaseProgram,
+		// Exposed for resource monitoring & error feedback via renderer.info:
+		programs: programs
+	};
 
 }
 
@@ -24257,63 +24284,65 @@ function WebGLRenderer( parameters ) {
 
 	// internal properties
 
-	let _this = this,
+	const _this = this;
 
-		_isContextLost = false,
+	let _isContextLost = false;
 
-		// internal state cache
+	// internal state cache
 
-		_framebuffer = null,
+	let _framebuffer = null;
 
-		_currentActiveCubeFace = 0,
-		_currentActiveMipmapLevel = 0,
-		_currentRenderTarget = null,
-		_currentFramebuffer = null,
-		_currentMaterialId = - 1,
+	let _currentActiveCubeFace = 0;
+	let _currentActiveMipmapLevel = 0;
+	let _currentRenderTarget = null;
+	let _currentFramebuffer = null;
+	let _currentMaterialId = - 1;
 
-		// geometry and program caching
+	// geometry and program caching
 
-		_currentGeometryProgram = {
-			geometry: null,
-			program: null,
-			wireframe: false
-		},
+	const _currentGeometryProgram = {
+		geometry: null,
+		program: null,
+		wireframe: false
+	};
 
-		_currentCamera = null,
-		_currentArrayCamera = null,
+	let _currentCamera = null;
+	let _currentArrayCamera = null;
 
-		_currentViewport = new Vector4(),
-		_currentScissor = new Vector4(),
-		_currentScissorTest = null,
+	const _currentViewport = new Vector4();
+	const _currentScissor = new Vector4();
+	let _currentScissorTest = null;
 
-		//
+	//
+
+	let _width = _canvas.width;
+	let _height = _canvas.height;
 
-		_width = _canvas.width,
-		_height = _canvas.height,
+	let _pixelRatio = 1;
+	let _opaqueSort = null;
+	let _transparentSort = null;
 
-		_pixelRatio = 1,
-		_opaqueSort = null,
-		_transparentSort = null,
+	const _viewport = new Vector4( 0, 0, _width, _height );
+	const _scissor = new Vector4( 0, 0, _width, _height );
+	let _scissorTest = false;
 
-		_viewport = new Vector4( 0, 0, _width, _height ),
-		_scissor = new Vector4( 0, 0, _width, _height ),
-		_scissorTest = false,
+	// frustum
 
-		// frustum
+	const _frustum = new Frustum();
 
-		_frustum = new Frustum(),
+	// clipping
 
-		// clipping
+	const _clipping = new WebGLClipping();
+	let _clippingEnabled = false;
+	let _localClippingEnabled = false;
 
-		_clipping = new WebGLClipping(),
-		_clippingEnabled = false,
-		_localClippingEnabled = false,
+	// camera matrices cache
 
-		// camera matrices cache
+	const _projScreenMatrix = new Matrix4();
 
-		_projScreenMatrix = new Matrix4(),
+	const _vector3 = new Vector3();
 
-		_vector3 = new Vector3();
+	const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
 
 	function getTargetPixelRatio() {
 
@@ -24852,11 +24881,9 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	const tempScene = new Scene();
-
 	this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
 
-		if ( scene === null ) scene = tempScene; // renderBufferDirect second parameter used to be fog (could be null)
+		if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)
 
 		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
@@ -25278,14 +25305,14 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		if ( ! ( camera && camera.isCamera ) ) {
+		if ( camera !== undefined && camera.isCamera !== true ) {
 
 			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
 			return;
 
 		}
 
-		if ( _isContextLost ) return;
+		if ( _isContextLost === true ) return;
 
 		// reset caching for this frame
 
@@ -25303,14 +25330,14 @@ function WebGLRenderer( parameters ) {
 
 		if ( camera.parent === null ) camera.updateMatrixWorld();
 
-		if ( xr.enabled && xr.isPresenting ) {
+		if ( xr.enabled === true && xr.isPresenting === true ) {
 
 			camera = xr.getCamera( camera );
 
 		}
 
 		//
-		if ( scene.isScene ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
+		if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
 
 		currentRenderState = renderStates.get( scene, camera );
 		currentRenderState.init();
@@ -25336,7 +25363,7 @@ function WebGLRenderer( parameters ) {
 
 		//
 
-		if ( _clippingEnabled ) _clipping.beginShadows();
+		if ( _clippingEnabled === true ) _clipping.beginShadows();
 
 		const shadowsArray = currentRenderState.state.shadowsArray;
 
@@ -25344,11 +25371,11 @@ function WebGLRenderer( parameters ) {
 
 		currentRenderState.setupLights( camera );
 
-		if ( _clippingEnabled ) _clipping.endShadows();
+		if ( _clippingEnabled === true ) _clipping.endShadows();
 
 		//
 
-		if ( this.info.autoReset ) this.info.reset();
+		if ( this.info.autoReset === true ) this.info.reset();
 
 		if ( renderTarget !== undefined ) {
 
@@ -25365,28 +25392,12 @@ function WebGLRenderer( parameters ) {
 		const opaqueObjects = currentRenderList.opaque;
 		const transparentObjects = currentRenderList.transparent;
 
-		if ( scene.overrideMaterial ) {
-
-			const overrideMaterial = scene.overrideMaterial;
-
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );
-
-		} else {
-
-			// opaque pass (front-to-back order)
-
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );
-
-			// transparent pass (back-to-front order)
-
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );
-
-		}
+		if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );
+		if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );
 
 		//
 
-		if ( scene.isScene ) scene.onAfterRender( _this, scene, camera );
+		if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );
 
 		//
 
@@ -25542,7 +25553,9 @@ function WebGLRenderer( parameters ) {
 
 	}
 
-	function renderObjects( renderList, scene, camera, overrideMaterial ) {
+	function renderObjects( renderList, scene, camera ) {
+
+		const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
 
 		for ( let i = 0, l = renderList.length; i < l; i ++ ) {
 
@@ -25550,7 +25563,7 @@ function WebGLRenderer( parameters ) {
 
 			const object = renderItem.object;
 			const geometry = renderItem.geometry;
-			const material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+			const material = overrideMaterial === null ? renderItem.material : overrideMaterial;
 			const group = renderItem.group;
 
 			if ( camera.isArrayCamera ) {
@@ -25620,6 +25633,8 @@ function WebGLRenderer( parameters ) {
 
 	function initMaterial( material, scene, object ) {
 
+		if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
+
 		const materialProperties = properties.get( material );
 
 		const lights = currentRenderState.state.lights;
@@ -25761,6 +25776,8 @@ function WebGLRenderer( parameters ) {
 
 	function setProgram( camera, scene, material, object ) {
 
+		if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
+
 		textures.resetTextureUnits();
 
 		const fog = scene.fog;
@@ -25770,9 +25787,9 @@ function WebGLRenderer( parameters ) {
 		const materialProperties = properties.get( material );
 		const lights = currentRenderState.state.lights;
 
-		if ( _clippingEnabled ) {
+		if ( _clippingEnabled === true ) {
 
-			if ( _localClippingEnabled || camera !== _currentCamera ) {
+			if ( _localClippingEnabled === true || camera !== _currentCamera ) {
 
 				const useCache =
 					camera === _currentCamera &&

+ 1 - 1
docs/api/en/math/Box3.html

@@ -42,7 +42,7 @@
 		[page:Vector3 min] - (optional) [page:Vector3] representing the lower (x, y, z) boundary of the box.
 		Default is ( + Infinity, + Infinity, + Infinity ).<br>
 
-		[page:Vector3 max] - (optional) [page:Vector3] representing the lower upper (x, y, z) boundary of the box.
+		[page:Vector3 max] - (optional) [page:Vector3] representing the upper (x, y, z) boundary of the box.
 		Default is ( - Infinity, - Infinity, - Infinity ).<br /><br />
 
 		Creates a [name] bounded by min and max.

+ 8 - 0
docs/api/en/math/Color.html

@@ -179,6 +179,14 @@ var color = new THREE.Color( 1, 0, 0 );
 		Sets this color's components based on an array formatted like [ [page:Float r], [page:Float g], [page:Float b] ].
 		</p>
 
+		<h3>[method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param:Integer index] )</h3>
+		<p>
+		[page:BufferAttribute attribute] - the source attribute.<br />
+		[page:Integer index] - index in the attribute.<br /><br />
+
+		Sets this color's components from the [page:BufferAttribute attribute].
+		</p>
+
 		<h3>[method:Integer getHex]()</h3>
 		<p>Returns the hexadecimal value of this color.</p>
 

+ 94 - 0
docs/api/en/textures/DataTexture2DArray.html

@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Texture] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">Creates an array of textures directly from raw data, width and height and depth.</p>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( data, width, height, depth )</h3>
+		<p>
+			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
+			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.
+		</p>
+		<p>
+			The interpretation of the data depends on type and format:
+			If the type is THREE.UnsignedByteType, a Uint8Array will be useful for addressing the texel data.
+			If the format is THREE.RGBAFormat, data needs four values for one texel; Red, Green, Blue and Alpha (typically the opacity). Similarly, THREE.RGBFormat specifies a format where only three values are used for each texel.<br />
+
+			For the packed types, THREE.UnsignedShort4444Type, THREE.UnsignedShort5551Type or THREE.UnsignedShort565Type, all color components of one texel can be addressed as bitfields within an integer element of a Uint16Array.<br />
+
+			In order to use the types THREE.FloatType and THREE.HalfFloatType, the WebGL implementation must support the respective extensions OES_texture_float and OES_texture_half_float. In order to use THREE.LinearFilter for component-wise, bilinear interpolation of the texels based on these types, the WebGL extensions OES_texture_float_linear or OES_texture_half_float_linear must also be present.
+		</p>
+
+		<h2>Code Example</h2>
+
+		<p>This creates a [name] where each texture has a different color.</p>
+
+		<code>
+		// create a buffer with color data
+
+		var size = width * height;
+		var data = new Uint8Array( 3 * size * depth );
+
+
+		for ( var i = 0; i < depth; i ++ ) {
+
+			var color = new THREE.Color( Math.random(), Math.random(), Math.random() );
+			var r = Math.floor( color.r * 255 );
+			var g = Math.floor( color.g * 255 );
+			var b = Math.floor( color.b * 255 );
+
+			for ( var j = 0; j < size; j ++ ) {
+
+				var stride = ( i * size + j ) * 3;
+
+				data[ stride ] = r;
+				data[ stride + 1 ] = g;
+				data[ stride + 2 ] = b;
+
+			}
+		}
+
+		// used the buffer to create a [name]
+
+		var texture = new THREE.DataTexture2DArray( data, width, height, depth );
+		texture.format = THREE.RGBFormat;
+		texture.type = THREE.UnsignedByteType;
+		</code>
+
+		<h2>Properties</h2>
+
+		<p>
+		See the base [page:Texture Texture] class for common properties.
+		</p>
+
+		<h3>[property:Image image]</h3>
+		<p>
+		Overridden with a record type holding data, width and height and depth.
+		</p>
+
+		<h2>Methods</h2>
+
+		<p>
+		See the base [page:Texture Texture] class for common methods.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		</p>
+	</body>
+</html>

+ 8 - 0
docs/api/zh/math/Color.html

@@ -177,6 +177,14 @@
 		从格式为[ [page:Float r], [page:Float g], [page:Float b] ]的数组数据中来创建Color对象。
 		</p>
 
+		<h3>[method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param:Integer index] )</h3>
+		<p>
+		[page:BufferAttribute attribute] - the source attribute.<br />
+		[page:Integer index] - index in the attribute.<br /><br />
+
+		Sets this color's components from the [page:BufferAttribute attribute].
+		</p>
+
 		<h3>[method:Integer getHex]()</h3>
 		<p>返回此颜色的十六进制值。</p>
 

+ 94 - 0
docs/api/zh/textures/DataTexture2DArray.html

@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Texture] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">Creates an array of textures directly from raw data, width and height and depth.</p>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( data, width, height, depth )</h3>
+		<p>
+			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
+			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.
+		</p>
+		<p>
+			The interpretation of the data depends on type and format:
+			If the type is THREE.UnsignedByteType, a Uint8Array will be useful for addressing the texel data.
+			If the format is THREE.RGBAFormat, data needs four values for one texel; Red, Green, Blue and Alpha (typically the opacity). Similarly, THREE.RGBFormat specifies a format where only three values are used for each texel.<br />
+
+			For the packed types, THREE.UnsignedShort4444Type, THREE.UnsignedShort5551Type or THREE.UnsignedShort565Type, all color components of one texel can be addressed as bitfields within an integer element of a Uint16Array.<br />
+
+			In order to use the types THREE.FloatType and THREE.HalfFloatType, the WebGL implementation must support the respective extensions OES_texture_float and OES_texture_half_float. In order to use THREE.LinearFilter for component-wise, bilinear interpolation of the texels based on these types, the WebGL extensions OES_texture_float_linear or OES_texture_half_float_linear must also be present.
+		</p>
+
+		<h2>代码示例</h2>
+
+		<p>This creates a [name] where each texture has a different color.</p>
+
+		<code>
+		// create a buffer with color data
+
+		var size = width * height;
+		var data = new Uint8Array( 3 * size * depth );
+
+
+		for ( var i = 0; i < depth; i ++ ) {
+
+			var color = new THREE.Color( Math.random(), Math.random(), Math.random() );
+			var r = Math.floor( color.r * 255 );
+			var g = Math.floor( color.g * 255 );
+			var b = Math.floor( color.b * 255 );
+
+			for ( var j = 0; j < size; j ++ ) {
+
+				var stride = ( i * size + j ) * 3;
+
+				data[ stride ] = r;
+				data[ stride + 1 ] = g;
+				data[ stride + 2 ] = b;
+
+			}
+		}
+
+		// used the buffer to create a [name]
+
+		var texture = new THREE.DataTexture2DArray( data, width, height, depth );
+		texture.format = THREE.RGBFormat;
+		texture.type = THREE.UnsignedByteType;
+		</code>
+
+		<h2>Properties</h2>
+
+		<p>
+		See the base [page:Texture Texture] class for common properties.
+		</p>
+
+		<h3>[property:Image image]</h3>
+		<p>
+		Overridden with a record type holding data, width and height and depth.
+		</p>
+
+		<h2>Methods</h2>
+
+		<p>
+		See the base [page:Texture Texture] class for common methods.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		</p>
+	</body>
+</html>

+ 10 - 0
docs/examples/en/controls/PointerLockControls.html

@@ -87,6 +87,16 @@
 			Whether or not the controls are locked.
 		</p>
 
+		<h3>[property:Float maxPolarAngle]</h3>
+		<p>
+			Camera pitch, upper limit. Range is 0 to Math.PI radians. Default is Math.PI.
+		</p>
+
+		<h3>[property:Float minPolarAngle]</h3>
+		<p>
+			Camera pitch, lower limit. Range is 0 to Math.PI radians. Default is 0.
+		</p>
+
 		<h2>Methods</h2>
 
 		<p>See the base [page:EventDispatcher] class for common methods.</p>

+ 10 - 0
docs/examples/zh/controls/PointerLockControls.html

@@ -87,6 +87,16 @@
 			Whether or not the controls are locked.
 		</p>
 
+		<h3>[property:Float maxPolarAngle]</h3>
+		<p>
+			Camera pitch, upper limit. Range is 0 to Math.PI radians. Default is Math.PI.
+		</p>
+
+		<h3>[property:Float minPolarAngle]</h3>
+		<p>
+			Camera pitch, lower limit. Range is 0 to Math.PI radians. Default is 0.
+		</p>
+
 		<h2>Methods</h2>
 
 		<p>See the base [page:EventDispatcher] class for common methods.</p>

+ 2 - 0
docs/list.js

@@ -338,6 +338,7 @@ var list = {
 				"CompressedTexture": "api/en/textures/CompressedTexture",
 				"CubeTexture": "api/en/textures/CubeTexture",
 				"DataTexture": "api/en/textures/DataTexture",
+				"DataTexture2DArray": "api/en/textures/DataTexture2DArray",
 				"DataTexture3D": "api/en/textures/DataTexture3D",
 				"DepthTexture": "api/en/textures/DepthTexture",
 				"Texture": "api/en/textures/Texture",
@@ -796,6 +797,7 @@ var list = {
 				"CompressedTexture": "api/zh/textures/CompressedTexture",
 				"CubeTexture": "api/zh/textures/CubeTexture",
 				"DataTexture": "api/zh/textures/DataTexture",
+				"DataTexture2DArray": "api/zh/textures/DataTexture2DArray",
 				"DataTexture3D": "api/zh/textures/DataTexture3D",
 				"DepthTexture": "api/zh/textures/DepthTexture",
 				"Texture": "api/zh/textures/Texture",

+ 14 - 21
editor/css/main.css

@@ -423,27 +423,21 @@ select {
 
 #toolbar {
 	position: absolute;
-	left: calc(50% - 290px); /* ( ( 100% - 300px ) / 2.0 ) - 140px */
-	bottom: 16px;
-	height: 32px;
+	left: 10px;
+	top: 42px;
+	width: 32px;
 	background: #eee;
-	color: #333;
+	text-align: center;
 }
 
-	#toolbar * {
-		vertical-align: middle;
+	#toolbar button, #toolbar input {
+		height: 32px;
 	}
 
-	#toolbar .Panel {
-		padding: 4px;
-		color: #888;
-	}
-
-	#toolbar button {
-		margin-right: 6px;
-		line-height: 14px;
-		height: 24px;
-	}
+		#toolbar button img {
+			width: 16px;
+			opacity: 0.5;
+		}
 
 .Outliner {
 	color: #444;
@@ -537,11 +531,6 @@ select {
 		bottom: 0;
 	}
 
-	#toolbar {
-		left: calc(50% - 140px);
-		top: 68px;
-	}
-
 }
 
 /* DARK MODE */
@@ -631,6 +620,10 @@ select {
 		background-color: #111;
 	}
 
+		#toolbar img {
+			filter: invert(1);
+		}
+
 	.Outliner {
 		color: #888;
 		background: #222;

+ 16 - 0
editor/images/rotate.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+	<g>
+		<path d="M256,64c-63.749,0-116.539,27.751-153.514,61.856l1.762-105.739C104.432,9.191,95.716,0.178,84.784,0
+			c-0.112,0-0.224,0-0.336,0C73.673,0,64.845,8.65,64.66,19.464l-2.639,158.351c-0.086,5.364,2.006,10.537,5.806,14.331
+			c3.794,3.794,8.94,5.885,14.331,5.793l154.392-2.639c10.926-0.191,19.635-9.198,19.451-20.13
+			c-0.185-10.814-9.013-19.457-19.787-19.457c-0.112,0-0.224,0-0.343,0l-109.13,1.867c30.515-28.919,75.54-53.991,129.26-53.991
+			c110.113,0,184.082,95.182,184.082,184.082c0,89.217-73.969,184.742-184.082,184.742c-120.109,0-184.082-107.296-184.082-184.742
+			c0-10.933-8.861-19.794-19.794-19.794S32.33,276.737,32.33,287.67C32.33,396.008,122.207,512,256,512
+			s223.67-115.992,223.67-224.336C479.67,179.649,389.793,64,256,64z"/>
+	</g>
+</g>
+</svg>

+ 60 - 0
editor/images/scale.svg

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+	<g>
+		<path d="M502.772,13.32c-6.85-6.85-17.943-6.85-24.793,0L324.846,166.453c-6.85,6.844-6.85,17.949,0,24.793
+			c3.425,3.425,7.908,5.138,12.397,5.138c4.483,0,8.972-1.713,12.397-5.138L502.772,38.114
+			C509.622,31.269,509.622,20.164,502.772,13.32z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M502.772,13.32c-3.285-3.291-7.75-5.138-12.397-5.138c-0.041,0-0.088,0-0.129,0L337.114,9.352
+			c-9.691,0.076-17.482,7.984-17.406,17.669c0.076,9.638,7.914,17.4,17.534,17.4c0.035,0,0.082,0,0.129,0l135.335-1.035
+			l-1.035,135.329c-0.07,9.685,7.721,17.593,17.406,17.669c0.041,0,0.082,0,0.129,0c9.62,0,17.458-7.762,17.534-17.4l1.169-153.132
+			C507.944,21.152,506.092,16.64,502.772,13.32z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M502.772,482.069L349.639,328.943c-6.85-6.85-17.943-6.85-24.793,0c-6.85,6.85-6.85,17.949,0,24.793l153.132,153.127
+			c3.425,3.425,7.908,5.138,12.397,5.138c4.483,0,8.972-1.713,12.397-5.138C509.622,500.012,509.622,488.913,502.772,482.069z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M506.74,341.211c-0.07-9.685-7.914-17.283-17.663-17.406c-9.691,0.07-17.482,7.984-17.406,17.663l1.029,135.329
+			l-135.329-1.029c-0.041,0-0.088,0-0.129,0c-9.62,0-17.458,7.768-17.534,17.406c-0.07,9.679,7.721,17.587,17.406,17.663
+			L490.246,512c0.035,0,0.082,0,0.129,0c4.647,0,9.106-1.847,12.397-5.132c3.32-3.326,5.173-7.838,5.138-12.531L506.74,341.211z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M190.662,158.27L37.535,5.138c-6.85-6.844-17.955-6.85-24.799,0c-6.85,6.844-6.85,17.949,0,24.793l153.127,153.132
+			c3.425,3.425,7.914,5.138,12.403,5.138c4.483,0,8.972-1.713,12.397-5.138C197.512,176.219,197.512,165.114,190.662,158.27z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M178.4,1.169L25.267,0c-0.047,0-0.088,0-0.134,0c-4.652,0-9.112,1.847-12.397,5.138c-3.326,3.32-5.173,7.832-5.143,12.531
+			l1.169,153.132c0.076,9.638,7.914,17.4,17.534,17.4c0.047,0,0.094,0,0.14,0c9.685-0.076,17.476-7.984,17.4-17.669L42.802,35.203
+			l135.329,1.034c0.041,0,0.088,0,0.134,0c9.62,0,17.458-7.762,17.534-17.4C195.876,9.153,188.085,1.245,178.4,1.169z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M187.155,328.943c-6.85-6.85-17.955-6.85-24.799,0L9.223,482.069c-6.844,6.844-6.844,17.943,0,24.793
+			c3.425,3.425,7.914,5.138,12.403,5.138c4.483,0,8.972-1.713,12.397-5.137l153.132-153.127
+			C193.999,346.892,194.005,335.793,187.155,328.943z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M174.624,475.763l-135.329,1.029l1.035-135.329c0.076-9.679-7.715-17.587-17.4-17.663c-0.041,0-0.088,0-0.134,0
+			c-9.62,0-17.458,7.768-17.534,17.406L4.092,494.331c-0.035,4.693,1.812,9.205,5.132,12.531c3.291,3.296,7.756,5.138,12.403,5.138
+			c0.041,0,0.088,0,0.134,0l153.132-1.169c9.685-0.07,17.476-7.984,17.4-17.663C192.217,483.478,184.168,475.78,174.624,475.763z"/>
+	</g>
+</g>
+</svg>

+ 46 - 0
editor/images/translate.svg

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+	<g>
+		<path d="M47.059,256l61.907-61.369c7.622-7.563,7.68-19.871,0.117-27.499c-7.563-7.628-19.877-7.68-27.499-0.117l-75.828,75.18
+			C2.074,245.844,0,250.815,0,256s2.074,10.156,5.755,13.811l75.828,75.18c3.791,3.753,8.736,5.632,13.688,5.632
+			c5.003,0,10.007-1.918,13.805-5.755c7.563-7.628,7.505-19.936-0.117-27.499L47.059,256z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M344.991,81.583l-75.18-75.828C266.156,2.074,261.185,0,256,0s-10.156,2.074-13.811,5.749l-75.18,75.828
+			c-7.563,7.628-7.505,19.936,0.117,27.499c7.628,7.557,19.942,7.505,27.499-0.117L256,47.059l61.369,61.9
+			c3.804,3.837,8.808,5.755,13.811,5.755c4.945,0,9.896-1.873,13.688-5.632C352.496,101.519,352.548,89.211,344.991,81.583z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M344.874,402.918c-7.635-7.557-19.942-7.505-27.499,0.123l-61.369,61.907l-61.375-61.907
+			c-7.557-7.635-19.871-7.667-27.499-0.123c-7.622,7.563-7.68,19.871-0.117,27.499l75.18,75.828
+			c3.649,3.688,8.62,5.755,13.805,5.755s10.156-2.067,13.818-5.755l75.18-75.828C352.554,422.789,352.502,410.481,344.874,402.918z"
+			/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M506.245,242.189l-75.828-75.18c-7.635-7.563-19.942-7.505-27.499,0.117c-7.557,7.628-7.505,19.936,0.123,27.499
+			L464.948,256l-61.907,61.369c-7.628,7.563-7.68,19.871-0.123,27.499c3.804,3.837,8.808,5.755,13.811,5.755
+			c4.945,0,9.897-1.88,13.688-5.632l75.828-75.18C509.933,266.156,512,261.185,512,256S509.933,245.844,506.245,242.189z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M492.557,236.557H19.443C8.704,236.557,0,245.261,0,256c0,10.739,8.704,19.443,19.443,19.443h473.114
+			c10.739,0,19.443-8.704,19.443-19.443C512,245.261,503.296,236.557,492.557,236.557z"/>
+	</g>
+</g>
+<g>
+	<g>
+		<path d="M256,0c-10.739,0-19.443,8.704-19.443,19.443v473.114c0,10.739,8.704,19.443,19.443,19.443
+			c10.739,0,19.443-8.704,19.443-19.443V19.443C275.443,8.704,266.739,0,256,0z"/>
+	</g>
+</g>
+</svg>

+ 1 - 1
editor/js/Loader.js

@@ -521,7 +521,7 @@ var Loader = function ( editor ) {
 
 			default:
 
-				// alert( 'Unsupported file format (' + extension +  ').' );
+				console.error( 'Unsupported file format (' + extension + ').' );
 
 				break;
 

+ 12 - 0
editor/js/Menubar.Help.js

@@ -32,6 +32,18 @@ var MenubarHelp = function ( editor ) {
 	} );
 	options.add( option );
 
+	// Icon
+
+	var option = new UIRow();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/help/icons' ) );
+	option.onClick( function () {
+
+		window.open( 'https://www.flaticon.com/packs/interface-44', '_blank' );
+
+	} );
+	options.add( option );
+
 	// About
 
 	var option = new UIRow();

+ 3 - 0
editor/js/Strings.js

@@ -76,6 +76,7 @@ var Strings = function ( config ) {
 
 			'menubar/help': 'Help',
 			'menubar/help/source_code': 'Source Code',
+			'menubar/help/icons': 'Icon Pack',
 			'menubar/help/about': 'About',
 
 			'sidebar/scene': 'Scene',
@@ -390,6 +391,7 @@ var Strings = function ( config ) {
 
 			'menubar/help': 'Aide',
 			'menubar/help/source_code': 'Code Source',
+			'menubar/help/icons': 'Icon Pack',
 			'menubar/help/about': 'A propos',
 
 			'sidebar/scene': 'Scène',
@@ -704,6 +706,7 @@ var Strings = function ( config ) {
 
 			'menubar/help': '帮助',
 			'menubar/help/source_code': '源码',
+			'menubar/help/icons': '图标组件包',
 			'menubar/help/about': '关于',
 
 			'sidebar/scene': '场景',

+ 25 - 20
editor/js/Toolbar.js

@@ -2,8 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-import { UIPanel, UIButton } from './libs/ui.js';
-import { UIBoolean } from './libs/ui.three.js';
+import { UIPanel, UIButton, UICheckbox } from './libs/ui.js';
 
 var Toolbar = function ( editor ) {
 
@@ -12,54 +11,60 @@ var Toolbar = function ( editor ) {
 
 	var container = new UIPanel();
 	container.setId( 'toolbar' );
-	container.setDisplay( 'none' );
-
-	var buttons = new UIPanel();
-	container.add( buttons );
 
 	// translate / rotate / scale
 
-	var translate = new UIButton( strings.getKey( 'toolbar/translate' ) );
+	var translateIcon = document.createElement( 'img' );
+	translateIcon.title = strings.getKey( 'toolbar/translate' );
+	translateIcon.src = 'images/translate.svg';
+
+	var translate = new UIButton();
 	translate.dom.className = 'Button selected';
+	translate.dom.appendChild( translateIcon );
 	translate.onClick( function () {
 
 		signals.transformModeChanged.dispatch( 'translate' );
 
 	} );
-	buttons.add( translate );
+	container.add( translate );
 
-	var rotate = new UIButton( strings.getKey( 'toolbar/rotate' ) );
+	var rotateIcon = document.createElement( 'img' );
+	rotateIcon.title = strings.getKey( 'toolbar/rotate' );
+	rotateIcon.src = 'images/rotate.svg';
+
+	var rotate = new UIButton();
+	rotate.dom.appendChild( rotateIcon );
 	rotate.onClick( function () {
 
 		signals.transformModeChanged.dispatch( 'rotate' );
 
 	} );
-	buttons.add( rotate );
+	container.add( rotate );
+
+	var scaleIcon = document.createElement( 'img' );
+	scaleIcon.title = strings.getKey( 'toolbar/scale' );
+	scaleIcon.src = 'images/scale.svg';
 
-	var scale = new UIButton( strings.getKey( 'toolbar/scale' ) );
+	var scale = new UIButton();
+	scale.dom.appendChild( scaleIcon );
 	scale.onClick( function () {
 
 		signals.transformModeChanged.dispatch( 'scale' );
 
 	} );
-	buttons.add( scale );
+	container.add( scale );
 
-	var local = new UIBoolean( false, strings.getKey( 'toolbar/local' ) );
+	var local = new UICheckbox( false );
+	local.dom.title = strings.getKey( 'toolbar/local' );
 	local.onChange( function () {
 
 		signals.spaceChanged.dispatch( this.getValue() === true ? 'local' : 'world' );
 
 	} );
-	buttons.add( local );
+	container.add( local );
 
 	//
 
-	signals.objectSelected.add( function ( object ) {
-
-		container.setDisplay( object === null ? 'none' : '' );
-
-	} );
-
 	signals.transformModeChanged.add( function ( mode ) {
 
 		translate.dom.classList.remove( 'selected' );

+ 4 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r117
+// r117.1
 
 const assets = [
 	'./',
@@ -56,6 +56,9 @@ const assets = [
 
 	'./manifest.json',
 	'./images/icon.png',
+	'./images/rotate.svg',
+	'./images/scale.svg',
+	'./images/translate.svg',
 
 	'./js/libs/codemirror/codemirror.css',
 	'./js/libs/codemirror/theme/monokai.css',

+ 6 - 1
examples/js/controls/PointerLockControls.js

@@ -16,6 +16,11 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 	this.domElement = domElement;
 	this.isLocked = false;
 
+	// Set to constrain the pitch of the camera
+	// Range is 0 to Math.PI radians
+	this.minPolarAngle = 0; // radians
+	this.maxPolarAngle = Math.PI; // radians
+
 	//
 	// internals
 	//
@@ -44,7 +49,7 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 		euler.y -= movementX * 0.002;
 		euler.x -= movementY * 0.002;
 
-		euler.x = Math.max( - PI_2, Math.min( PI_2, euler.x ) );
+		euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );
 
 		camera.quaternion.setFromEuler( euler );
 

+ 6 - 4
examples/js/controls/TransformControls.js

@@ -738,7 +738,8 @@ THREE.TransformControlsGizmo = function () {
 		depthWrite: false,
 		transparent: true,
 		side: THREE.DoubleSide,
-		fog: false
+		fog: false,
+		toneMapped: false
 	} );
 
 	var gizmoLineMaterial = new THREE.LineBasicMaterial( {
@@ -746,7 +747,8 @@ THREE.TransformControlsGizmo = function () {
 		depthWrite: false,
 		transparent: true,
 		linewidth: 1,
-		fog: false
+		fog: false,
+		toneMapped: false
 	} );
 
 	// Make unique material for each axis/color
@@ -811,7 +813,7 @@ THREE.TransformControlsGizmo = function () {
 
 	var scaleHandleGeometry = new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 );
 
-	var lineGeometry = new THREE.BufferGeometry( );
+	var lineGeometry = new THREE.BufferGeometry();
 	lineGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,	1, 0, 0 ], 3 ) );
 
 	var CircleGeometry = function ( radius, arc ) {
@@ -1568,7 +1570,7 @@ THREE.TransformControlsPlane = function () {
 
 	THREE.Mesh.call( this,
 		new THREE.PlaneBufferGeometry( 100000, 100000, 2, 2 ),
-		new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1 } )
+		new THREE.MeshBasicMaterial( { visible: false, wireframe: true, side: THREE.DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } )
 	);
 
 	this.type = 'TransformControlsPlane';

+ 14 - 0
examples/js/loaders/EXRLoader.js

@@ -1928,6 +1928,16 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade
 
 		}
 
+		function parseV3f( dataView, offset ) {
+
+			var x = parseFloat32( dataView, offset );
+			var y = parseFloat32( dataView, offset );
+			var z = parseFloat32( dataView, offset );
+
+			return [ x, y, z ];
+
+		}
+
 		function parseValue( dataView, buffer, offset, type, size ) {
 
 			if ( type === 'string' || type === 'stringvector' || type === 'iccProfile' ) {
@@ -1962,6 +1972,10 @@ THREE.EXRLoader.prototype = Object.assign( Object.create( THREE.DataTextureLoade
 
 				return parseV2f( dataView, offset );
 
+			} else if ( type === 'v3f' ) {
+
+				return parseV3f( dataView, offset );
+
 			} else if ( type === 'int' ) {
 
 				return parseInt32( dataView, offset );

+ 17 - 3
examples/js/renderers/CSS2DRenderer.js

@@ -7,7 +7,8 @@ THREE.CSS2DObject = function ( element ) {
 
 	THREE.Object3D.call( this );
 
-	this.element = element;
+	this.element = element || document.createElement( 'div' );
+
 	this.element.style.position = 'absolute';
 
 	this.addEventListener( 'removed', function () {
@@ -26,8 +27,21 @@ THREE.CSS2DObject = function ( element ) {
 
 };
 
-THREE.CSS2DObject.prototype = Object.create( THREE.Object3D.prototype );
-THREE.CSS2DObject.prototype.constructor = THREE.CSS2DObject;
+THREE.CSS2DObject.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
+
+	constructor: THREE.CSS2DObject,
+
+	copy: function ( source, recursive ) {
+
+		THREE.Object3D.prototype.copy.call( this, source, recursive );
+
+		this.element = source.element.cloneNode( true );
+
+		return this;
+
+	}
+
+} );
 
 //
 

+ 16 - 3
examples/js/renderers/CSS3DRenderer.js

@@ -9,7 +9,7 @@ THREE.CSS3DObject = function ( element ) {
 
 	THREE.Object3D.call( this );
 
-	this.element = element;
+	this.element = element || document.createElement( 'div' );
 	this.element.style.position = 'absolute';
 	this.element.style.pointerEvents = 'auto';
 
@@ -29,8 +29,21 @@ THREE.CSS3DObject = function ( element ) {
 
 };
 
-THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );
-THREE.CSS3DObject.prototype.constructor = THREE.CSS3DObject;
+THREE.CSS3DObject.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
+
+	constructor: THREE.CSS3DObject,
+
+	copy: function ( source, recursive ) {
+
+		THREE.Object3D.prototype.copy.call( this, source, recursive );
+
+		this.element = source.element.cloneNode( true );
+
+		return this;
+
+	}
+
+} );
 
 THREE.CSS3DSprite = function ( element ) {
 

+ 3 - 0
examples/jsm/controls/PointerLockControls.d.ts

@@ -14,6 +14,9 @@ export class PointerLockControls extends EventDispatcher {
 
 	isLocked: boolean;
 
+	minPolarAngle: number;
+	maxPolarAngle: number;
+
 	connect(): void;
 	disconnect(): void;
 	dispose(): void;

+ 6 - 1
examples/jsm/controls/PointerLockControls.js

@@ -21,6 +21,11 @@ var PointerLockControls = function ( camera, domElement ) {
 	this.domElement = domElement;
 	this.isLocked = false;
 
+	// Set to constrain the pitch of the camera
+	// Range is 0 to Math.PI radians
+	this.minPolarAngle = 0; // radians
+	this.maxPolarAngle = Math.PI; // radians
+
 	//
 	// internals
 	//
@@ -49,7 +54,7 @@ var PointerLockControls = function ( camera, domElement ) {
 		euler.y -= movementX * 0.002;
 		euler.x -= movementY * 0.002;
 
-		euler.x = Math.max( - PI_2, Math.min( PI_2, euler.x ) );
+		euler.x = Math.max( PI_2 - scope.maxPolarAngle, Math.min( PI_2 - scope.minPolarAngle, euler.x ) );
 
 		camera.quaternion.setFromEuler( euler );
 

+ 6 - 4
examples/jsm/controls/TransformControls.js

@@ -760,7 +760,8 @@ var TransformControlsGizmo = function () {
 		depthWrite: false,
 		transparent: true,
 		side: DoubleSide,
-		fog: false
+		fog: false,
+		toneMapped: false
 	} );
 
 	var gizmoLineMaterial = new LineBasicMaterial( {
@@ -768,7 +769,8 @@ var TransformControlsGizmo = function () {
 		depthWrite: false,
 		transparent: true,
 		linewidth: 1,
-		fog: false
+		fog: false,
+		toneMapped: false
 	} );
 
 	// Make unique material for each axis/color
@@ -833,7 +835,7 @@ var TransformControlsGizmo = function () {
 
 	var scaleHandleGeometry = new BoxBufferGeometry( 0.125, 0.125, 0.125 );
 
-	var lineGeometry = new BufferGeometry( );
+	var lineGeometry = new BufferGeometry();
 	lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0,	1, 0, 0 ], 3 ) );
 
 	var CircleGeometry = function ( radius, arc ) {
@@ -1590,7 +1592,7 @@ var TransformControlsPlane = function () {
 
 	Mesh.call( this,
 		new PlaneBufferGeometry( 100000, 100000, 2, 2 ),
-		new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1 } )
+		new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1, toneMapped: false } )
 	);
 
 	this.type = 'TransformControlsPlane';

+ 1 - 1
examples/jsm/libs/stats.module.d.ts

@@ -18,7 +18,7 @@ declare namespace Stats {
 		update( value: number, maxValue: number ): void;
 	}
 
-	function Panel(): Panel;
+	function Panel( name?: string, fg?: string, bg?: string ): Panel;
 }
 
 export default Stats;

+ 14 - 0
examples/jsm/loaders/EXRLoader.js

@@ -1942,6 +1942,16 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype
 
 		}
 
+		function parseV3f( dataView, offset ) {
+
+			var x = parseFloat32( dataView, offset );
+			var y = parseFloat32( dataView, offset );
+			var z = parseFloat32( dataView, offset );
+
+			return [ x, y, z ];
+
+		}
+
 		function parseValue( dataView, buffer, offset, type, size ) {
 
 			if ( type === 'string' || type === 'stringvector' || type === 'iccProfile' ) {
@@ -1976,6 +1986,10 @@ EXRLoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype
 
 				return parseV2f( dataView, offset );
 
+			} else if ( type === 'v3f' ) {
+
+				return parseV3f( dataView, offset );
+
 			} else if ( type === 'int' ) {
 
 				return parseInt32( dataView, offset );

+ 17 - 3
examples/jsm/renderers/CSS2DRenderer.js

@@ -12,7 +12,8 @@ var CSS2DObject = function ( element ) {
 
 	Object3D.call( this );
 
-	this.element = element;
+	this.element = element || document.createElement( 'div' );
+
 	this.element.style.position = 'absolute';
 
 	this.addEventListener( 'removed', function () {
@@ -31,8 +32,21 @@ var CSS2DObject = function ( element ) {
 
 };
 
-CSS2DObject.prototype = Object.create( Object3D.prototype );
-CSS2DObject.prototype.constructor = CSS2DObject;
+CSS2DObject.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+	constructor: CSS2DObject,
+
+	copy: function ( source, recursive ) {
+
+		Object3D.prototype.copy.call( this, source, recursive );
+
+		this.element = source.element.cloneNode( true );
+
+		return this;
+
+	}
+
+} );
 
 //
 

+ 16 - 3
examples/jsm/renderers/CSS3DRenderer.js

@@ -14,7 +14,7 @@ var CSS3DObject = function ( element ) {
 
 	Object3D.call( this );
 
-	this.element = element;
+	this.element = element || document.createElement( 'div' );
 	this.element.style.position = 'absolute';
 	this.element.style.pointerEvents = 'auto';
 
@@ -34,8 +34,21 @@ var CSS3DObject = function ( element ) {
 
 };
 
-CSS3DObject.prototype = Object.create( Object3D.prototype );
-CSS3DObject.prototype.constructor = CSS3DObject;
+CSS3DObject.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+	constructor: CSS3DObject,
+
+	copy: function ( source, recursive ) {
+
+		Object3D.prototype.copy.call( this, source, recursive );
+
+		this.element = source.element.cloneNode( true );
+
+		return this;
+
+	}
+
+} );
 
 var CSS3DSprite = function ( element ) {
 

+ 13 - 13
package-lock.json

@@ -545,9 +545,9 @@
       "dev": true
     },
     "eslint": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.1.0.tgz",
-      "integrity": "sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.2.0.tgz",
+      "integrity": "sha512-B3BtEyaDKC5MlfDa2Ha8/D6DsS4fju95zs0hjS3HdGazw+LNayai38A25qMppK37wWGWNYSPOR6oYzlz5MHsRQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -556,10 +556,10 @@
         "cross-spawn": "^7.0.2",
         "debug": "^4.0.1",
         "doctrine": "^3.0.0",
-        "eslint-scope": "^5.0.0",
+        "eslint-scope": "^5.1.0",
         "eslint-utils": "^2.0.0",
-        "eslint-visitor-keys": "^1.1.0",
-        "espree": "^7.0.0",
+        "eslint-visitor-keys": "^1.2.0",
+        "espree": "^7.1.0",
         "esquery": "^1.2.0",
         "esutils": "^2.0.2",
         "file-entry-cache": "^5.0.1",
@@ -782,9 +782,9 @@
       }
     },
     "fast-deep-equal": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
-      "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
       "dev": true
     },
     "fast-json-stable-stringify": {
@@ -1486,7 +1486,7 @@
     },
     "path-is-absolute": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true
     },
@@ -1686,9 +1686,9 @@
       }
     },
     "rollup": {
-      "version": "2.13.1",
-      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.13.1.tgz",
-      "integrity": "sha512-EiICynxIO1DTFmFn+/98gfaqCToK2nbjPjHJLuNvpcwc+P035VrXmJxi3JsOhqkdty+0cOEhJ26ceGTY3UPMPQ==",
+      "version": "2.15.0",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.15.0.tgz",
+      "integrity": "sha512-HAk4kyXiV5sdNDnbKWk5zBPnkX/DAgx09Kbp8rRIRDVsTUVN3vnSowR7ZHkV6/lAiE6c2TQ8HtYb72aCPGW4Jw==",
       "dev": true,
       "requires": {
         "fsevents": "~2.1.2"

+ 2 - 2
package.json

@@ -83,12 +83,12 @@
     "@typescript-eslint/eslint-plugin": "^2.34.0",
     "@typescript-eslint/parser": "^2.34.0",
     "concurrently": "^5.2.0",
-    "eslint": "^7.1.0",
+    "eslint": "^7.2.0",
     "eslint-config-mdcs": "^5.0.0",
     "eslint-plugin-html": "^6.0.2",
     "google-closure-compiler": "20200224.0.0",
     "http-server": "^0.12.3",
-    "rollup": "^2.13.1",
+    "rollup": "^2.15.0",
     "rollup-plugin-buble": "^0.19.8",
     "typescript": "^3.9.5"
   },

+ 32 - 31
src/core/Geometry.js

@@ -186,7 +186,7 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		const scope = this;
 
-		const indices = geometry.index !== null ? geometry.index.array : undefined;
+		const index = geometry.index !== null ? geometry.index : undefined;
 		const attributes = geometry.attributes;
 
 		if ( attributes.position === undefined ) {
@@ -196,21 +196,21 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		}
 
-		const positions = attributes.position.array;
-		const normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
-		const colors = attributes.color !== undefined ? attributes.color.array : undefined;
-		const uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;
-		const uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;
+		const position = attributes.position;
+		const normal = attributes.normal;
+		const color = attributes.color;
+		const uv = attributes.uv;
+		const uv2 = attributes.uv2;
 
-		if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
+		if ( uv2 !== undefined ) this.faceVertexUvs[ 1 ] = [];
 
-		for ( let i = 0; i < positions.length; i += 3 ) {
+		for ( let i = 0; i < position.count; i ++ ) {
 
-			scope.vertices.push( new Vector3().fromArray( positions, i ) );
+			scope.vertices.push( new Vector3().fromBufferAttribute( position, i ) );
 
-			if ( colors !== undefined ) {
+			if ( color !== undefined ) {
 
-				scope.colors.push( new Color().fromArray( colors, i ) );
+				scope.colors.push( new Color().fromBufferAttribute( color, i ) );
 
 			}
 
@@ -218,37 +218,38 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		function addFace( a, b, c, materialIndex ) {
 
-			const vertexColors = ( colors === undefined ) ? [] : [
+			const vertexColors = ( color === undefined ) ? [] : [
 				scope.colors[ a ].clone(),
 				scope.colors[ b ].clone(),
-				scope.colors[ c ].clone() ];
+				scope.colors[ c ].clone()
+			];
 
-			const vertexNormals = ( normals === undefined ) ? [] : [
-				new Vector3().fromArray( normals, a * 3 ),
-				new Vector3().fromArray( normals, b * 3 ),
-				new Vector3().fromArray( normals, c * 3 )
+			const vertexNormals = ( normal === undefined ) ? [] : [
+				new Vector3().fromBufferAttribute( normal, a ),
+				new Vector3().fromBufferAttribute( normal, b ),
+				new Vector3().fromBufferAttribute( normal, c )
 			];
 
 			const face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );
 
 			scope.faces.push( face );
 
-			if ( uvs !== undefined ) {
+			if ( uv !== undefined ) {
 
 				scope.faceVertexUvs[ 0 ].push( [
-					new Vector2().fromArray( uvs, a * 2 ),
-					new Vector2().fromArray( uvs, b * 2 ),
-					new Vector2().fromArray( uvs, c * 2 )
+					new Vector2().fromBufferAttribute( uv, a ),
+					new Vector2().fromBufferAttribute( uv, b ),
+					new Vector2().fromBufferAttribute( uv, c )
 				] );
 
 			}
 
-			if ( uvs2 !== undefined ) {
+			if ( uv2 !== undefined ) {
 
 				scope.faceVertexUvs[ 1 ].push( [
-					new Vector2().fromArray( uvs2, a * 2 ),
-					new Vector2().fromArray( uvs2, b * 2 ),
-					new Vector2().fromArray( uvs2, c * 2 )
+					new Vector2().fromBufferAttribute( uv2, a ),
+					new Vector2().fromBufferAttribute( uv2, b ),
+					new Vector2().fromBufferAttribute( uv2, c )
 				] );
 
 			}
@@ -268,9 +269,9 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 				for ( let j = start, jl = start + count; j < jl; j += 3 ) {
 
-					if ( indices !== undefined ) {
+					if ( index !== undefined ) {
 
-						addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );
+						addFace( index.getX( j ), index.getX( j + 1 ), index.getX( j + 2 ), group.materialIndex );
 
 					} else {
 
@@ -284,17 +285,17 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		} else {
 
-			if ( indices !== undefined ) {
+			if ( index !== undefined ) {
 
-				for ( let i = 0; i < indices.length; i += 3 ) {
+				for ( let i = 0; i < index.count; i += 3 ) {
 
-					addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
+					addFace( index.getX( i ), index.getX( i + 1 ), index.getX( i + 2 ) );
 
 				}
 
 			} else {
 
-				for ( let i = 0; i < positions.length / 3; i += 3 ) {
+				for ( let i = 0; i < position.count; i += 3 ) {
 
 					addFace( i, i + 1, i + 2 );
 

+ 1 - 1
src/materials/Material.js

@@ -83,7 +83,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 	isMaterial: true,
 
-	onBeforeCompile: function () {},
+	onBeforeCompile: function ( /* shaderobject, renderer */ ) {},
 
 	setValues: function ( values ) {
 

+ 4 - 0
src/math/Color.d.ts

@@ -1,3 +1,5 @@
+import { BufferAttribute } from './../core/BufferAttribute';
+
 /**
  * @author Joe Pea / http://github.com/trusktr
  */
@@ -188,6 +190,8 @@ export class Color {
 	 */
 	toArray( xyz: ArrayLike<number>, offset?: number ): ArrayLike<number>;
 
+	fromBufferAttribute( attribute: BufferAttribute, index: number ): this;
+
 	/**
 	 * List of X11 color names.
 	 */

+ 20 - 0
src/math/Color.js

@@ -580,6 +580,26 @@ Object.assign( Color.prototype, {
 
 	},
 
+	fromBufferAttribute: function ( attribute, index ) {
+
+		this.r = attribute.getX( index );
+		this.g = attribute.getY( index );
+		this.b = attribute.getZ( index );
+
+		if ( attribute.normalized === true ) {
+
+			// assuming Uint8Array
+
+			this.r /= 255;
+			this.g /= 255;
+			this.b /= 255;
+
+		}
+
+		return this;
+
+	},
+
 	toJSON: function () {
 
 		return this.getHex();

+ 55 - 66
src/renderers/WebGLRenderer.js

@@ -22,7 +22,6 @@ import { UniformsLib } from './shaders/UniformsLib.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Vector3 } from '../math/Vector3.js';
 import { Vector4 } from '../math/Vector4.js';
-import { Scene } from '../scenes/Scene.js';
 import { WebGLAnimation } from './webgl/WebGLAnimation.js';
 import { WebGLAttributes } from './webgl/WebGLAttributes.js';
 import { WebGLBackground } from './webgl/WebGLBackground.js';
@@ -119,55 +118,57 @@ function WebGLRenderer( parameters ) {
 
 	// internal properties
 
-	let _this = this,
+	const _this = this;
 
-		_isContextLost = false,
+	let _isContextLost = false;
 
-		// internal state cache
+	// internal state cache
 
-		_framebuffer = null,
+	let _framebuffer = null;
 
-		_currentActiveCubeFace = 0,
-		_currentActiveMipmapLevel = 0,
-		_currentRenderTarget = null,
-		_currentFramebuffer = null,
-		_currentMaterialId = - 1,
+	let _currentActiveCubeFace = 0;
+	let _currentActiveMipmapLevel = 0;
+	let _currentRenderTarget = null;
+	let _currentFramebuffer = null;
+	let _currentMaterialId = - 1;
 
-		_currentCamera = null,
-		_currentArrayCamera = null,
+	let _currentCamera = null;
+	let _currentArrayCamera = null;
 
-		_currentViewport = new Vector4(),
-		_currentScissor = new Vector4(),
-		_currentScissorTest = null,
+	const _currentViewport = new Vector4();
+	const _currentScissor = new Vector4();
+	let _currentScissorTest = null;
 
-		//
+	//
+
+	let _width = _canvas.width;
+	let _height = _canvas.height;
 
-		_width = _canvas.width,
-		_height = _canvas.height,
+	let _pixelRatio = 1;
+	let _opaqueSort = null;
+	let _transparentSort = null;
 
-		_pixelRatio = 1,
-		_opaqueSort = null,
-		_transparentSort = null,
+	const _viewport = new Vector4( 0, 0, _width, _height );
+	const _scissor = new Vector4( 0, 0, _width, _height );
+	let _scissorTest = false;
 
-		_viewport = new Vector4( 0, 0, _width, _height ),
-		_scissor = new Vector4( 0, 0, _width, _height ),
-		_scissorTest = false,
+	// frustum
 
-		// frustum
+	const _frustum = new Frustum();
 
-		_frustum = new Frustum(),
+	// clipping
 
-		// clipping
+	const _clipping = new WebGLClipping();
+	let _clippingEnabled = false;
+	let _localClippingEnabled = false;
 
-		_clipping = new WebGLClipping(),
-		_clippingEnabled = false,
-		_localClippingEnabled = false,
+	// camera matrices cache
 
-		// camera matrices cache
+	const _projScreenMatrix = new Matrix4();
 
-		_projScreenMatrix = new Matrix4(),
+	const _vector3 = new Vector3();
 
-		_vector3 = new Vector3();
+	const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
 
 	function getTargetPixelRatio() {
 
@@ -709,11 +710,9 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	const tempScene = new Scene();
-
 	this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
 
-		if ( scene === null ) scene = tempScene; // renderBufferDirect second parameter used to be fog (could be null)
+		if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)
 
 		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
@@ -957,14 +956,14 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		if ( ! ( camera && camera.isCamera ) ) {
+		if ( camera !== undefined && camera.isCamera !== true ) {
 
 			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
 			return;
 
 		}
 
-		if ( _isContextLost ) return;
+		if ( _isContextLost === true ) return;
 
 		// reset caching for this frame
 
@@ -980,14 +979,14 @@ function WebGLRenderer( parameters ) {
 
 		if ( camera.parent === null ) camera.updateMatrixWorld();
 
-		if ( xr.enabled && xr.isPresenting ) {
+		if ( xr.enabled === true && xr.isPresenting === true ) {
 
 			camera = xr.getCamera( camera );
 
 		}
 
 		//
-		if ( scene.isScene ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
+		if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );
 
 		currentRenderState = renderStates.get( scene, camera );
 		currentRenderState.init();
@@ -1013,7 +1012,7 @@ function WebGLRenderer( parameters ) {
 
 		//
 
-		if ( _clippingEnabled ) _clipping.beginShadows();
+		if ( _clippingEnabled === true ) _clipping.beginShadows();
 
 		const shadowsArray = currentRenderState.state.shadowsArray;
 
@@ -1021,11 +1020,11 @@ function WebGLRenderer( parameters ) {
 
 		currentRenderState.setupLights( camera );
 
-		if ( _clippingEnabled ) _clipping.endShadows();
+		if ( _clippingEnabled === true ) _clipping.endShadows();
 
 		//
 
-		if ( this.info.autoReset ) this.info.reset();
+		if ( this.info.autoReset === true ) this.info.reset();
 
 		if ( renderTarget !== undefined ) {
 
@@ -1042,28 +1041,12 @@ function WebGLRenderer( parameters ) {
 		const opaqueObjects = currentRenderList.opaque;
 		const transparentObjects = currentRenderList.transparent;
 
-		if ( scene.overrideMaterial ) {
-
-			const overrideMaterial = scene.overrideMaterial;
-
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );
-
-		} else {
-
-			// opaque pass (front-to-back order)
-
-			if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );
-
-			// transparent pass (back-to-front order)
-
-			if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );
-
-		}
+		if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );
+		if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );
 
 		//
 
-		if ( scene.isScene ) scene.onAfterRender( _this, scene, camera );
+		if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );
 
 		//
 
@@ -1219,7 +1202,9 @@ function WebGLRenderer( parameters ) {
 
 	}
 
-	function renderObjects( renderList, scene, camera, overrideMaterial ) {
+	function renderObjects( renderList, scene, camera ) {
+
+		const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
 
 		for ( let i = 0, l = renderList.length; i < l; i ++ ) {
 
@@ -1227,7 +1212,7 @@ function WebGLRenderer( parameters ) {
 
 			const object = renderItem.object;
 			const geometry = renderItem.geometry;
-			const material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;
+			const material = overrideMaterial === null ? renderItem.material : overrideMaterial;
 			const group = renderItem.group;
 
 			if ( camera.isArrayCamera ) {
@@ -1295,6 +1280,8 @@ function WebGLRenderer( parameters ) {
 
 	function initMaterial( material, scene, object ) {
 
+		if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
+
 		const materialProperties = properties.get( material );
 
 		const lights = currentRenderState.state.lights;
@@ -1436,6 +1423,8 @@ function WebGLRenderer( parameters ) {
 
 	function setProgram( camera, scene, material, object ) {
 
+		if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
+
 		textures.resetTextureUnits();
 
 		const fog = scene.fog;
@@ -1445,9 +1434,9 @@ function WebGLRenderer( parameters ) {
 		const materialProperties = properties.get( material );
 		const lights = currentRenderState.state.lights;
 
-		if ( _clippingEnabled ) {
+		if ( _clippingEnabled === true ) {
 
-			if ( _localClippingEnabled || camera !== _currentCamera ) {
+			if ( _localClippingEnabled === true || camera !== _currentCamera ) {
 
 				const useCache =
 					camera === _currentCamera &&

+ 1 - 1
src/renderers/webgl/WebGLBackground.js

@@ -25,7 +25,7 @@ function WebGLBackground( renderer, state, objects, premultipliedAlpha ) {
 
 	function render( renderList, scene, camera, forceClear ) {
 
-		let background = scene.background;
+		let background = scene.isScene === true ? scene.background : null;
 
 		// Ignore background in AR
 		// TODO: Reconsider this.

+ 16 - 10
src/renderers/webgl/WebGLPrograms.js

@@ -142,7 +142,7 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
 
 	}
 
-	this.getParameters = function ( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
+	function getParameters( material, lights, shadows, scene, nClipPlanes, nClipIntersection, object ) {
 
 		const fog = scene.fog;
 		const environment = material.isMeshStandardMaterial ? scene.environment : null;
@@ -295,9 +295,9 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
 
 		return parameters;
 
-	};
+	}
 
-	this.getProgramCacheKey = function ( parameters ) {
+	function getProgramCacheKey( parameters ) {
 
 		const array = [];
 
@@ -340,9 +340,9 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
 
 		return array.join();
 
-	};
+	}
 
-	this.acquireProgram = function ( parameters, cacheKey ) {
+	function acquireProgram( parameters, cacheKey ) {
 
 		let program;
 
@@ -371,9 +371,9 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
 
 		return program;
 
-	};
+	}
 
-	this.releaseProgram = function ( program ) {
+	function releaseProgram( program ) {
 
 		if ( -- program.usedTimes === 0 ) {
 
@@ -387,10 +387,16 @@ function WebGLPrograms( renderer, extensions, capabilities, bindingStates ) {
 
 		}
 
-	};
+	}
 
-	// Exposed for resource monitoring & error feedback via renderer.info:
-	this.programs = programs;
+	return {
+		getParameters: getParameters,
+		getProgramCacheKey: getProgramCacheKey,
+		acquireProgram: acquireProgram,
+		releaseProgram: releaseProgram,
+		// Exposed for resource monitoring & error feedback via renderer.info:
+		programs: programs
+	};
 
 }
 

+ 2 - 1
test/package.json

@@ -4,7 +4,7 @@
   "description": "This package hiding test dependincies from main repo because puppeteer is pretty big.",
   "scripts": {
     "dev": "rollup -c rollup.unit.config.js -w -m inline",
-    "unit": "rollup -c rollup.unit.config.js && qunit -r failonlyreporter unit/build/three.source.unit.js"
+    "unit": "rollup -c rollup.unit.config.js && rimraf node_modules/three && qunit -r failonlyreporter unit/build/three.source.unit.js"
   },
   "devDependencies": {
     "failonlyreporter": "^1.0.0",
@@ -13,6 +13,7 @@
     "pixelmatch": "^5.2.0",
     "puppeteer": "3.1.0",
     "qunit": "^2.10.0",
+    "rimraf": "^3.0.2",
     "serve-handler": "^6.1.2"
   },
   "license": "MIT"

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels