Browse Source

Merge remote-tracking branch 'mrdoob/dev' into add-optional-lines-shader

Garrett Johnson 6 years ago
parent
commit
5ed4b42e86
83 changed files with 4264 additions and 745 deletions
  1. 50 37
      build/three.js
  2. 68 293
      build/three.min.js
  3. 50 37
      build/three.module.js
  4. 1 1
      docs/api/en/renderers/WebGLRenderer.html
  5. 53 11
      docs/index.html
  6. 10 2
      docs/manual/en/introduction/Import-via-modules.html
  7. 15 13
      docs/manual/en/introduction/Useful-links.html
  8. 20 12
      docs/manual/zh/introduction/Useful-links.html
  9. 89 38
      docs/page.css
  10. 2 2
      docs/prettify/threejs.css
  11. 5 4
      editor/manifest.json
  12. 2 2
      examples/css3d_panorama_deviceorientation.html
  13. 1 0
      examples/files.js
  14. 66 24
      examples/index.html
  15. 6 1
      examples/js/exporters/GLTFExporter.js
  16. 362 47
      examples/js/loaders/3MFLoader.js
  17. 4 2
      examples/js/loaders/GLTFLoader.js
  18. 34 7
      examples/js/postprocessing/EffectComposer.js
  19. 24 0
      examples/jsm/controls/DeviceOrientationControls.d.ts
  20. 120 0
      examples/jsm/controls/DeviceOrientationControls.js
  21. 20 0
      examples/jsm/controls/DragControls.d.ts
  22. 298 0
      examples/jsm/controls/DragControls.js
  23. 26 0
      examples/jsm/controls/EditorControls.d.ts
  24. 327 0
      examples/jsm/controls/EditorControls.js
  25. 25 0
      examples/jsm/controls/PointerLockControls.d.ts
  26. 134 0
      examples/jsm/controls/PointerLockControls.js
  27. 1 1
      examples/jsm/controls/TrackballControls.js
  28. 6 1
      examples/jsm/exporters/GLTFExporter.js
  29. 24 0
      examples/jsm/loaders/BVHLoader.d.ts
  30. 428 0
      examples/jsm/loaders/BVHLoader.js
  31. 4 2
      examples/jsm/loaders/GLTFLoader.js
  32. 17 0
      examples/jsm/loaders/PCDLoader.d.ts
  33. 321 0
      examples/jsm/loaders/PCDLoader.js
  34. 18 0
      examples/jsm/loaders/PLYLoader.d.ts
  35. 515 0
      examples/jsm/loaders/PLYLoader.js
  36. 16 0
      examples/jsm/loaders/STLLoader.d.ts
  37. 4 7
      examples/jsm/loaders/STLLoader.js
  38. 16 0
      examples/jsm/loaders/TGALoader.d.ts
  39. 558 0
      examples/jsm/loaders/TGALoader.js
  40. 3 2
      examples/jsm/utils/ShadowMapViewer.js
  41. 2 3
      examples/misc_controls_fly.html
  42. BIN
      examples/models/3mf/truck.3mf
  43. BIN
      examples/models/kmz/Box.kmz
  44. BIN
      examples/textures/memorial.exr
  45. BIN
      examples/textures/memorial.hdr
  46. 0 10
      examples/textures/miranda_uncropped.hdr
  47. 5 5
      examples/webgl_buffergeometry_lines.html
  48. 5 5
      examples/webgl_buffergeometry_lines_indexed.html
  49. 6 6
      examples/webgl_custom_attributes_lines.html
  50. 158 0
      examples/webgl_loader_3mf_materials.html
  51. 6 5
      examples/webgl_loader_texture_exr.html
  52. 6 5
      examples/webgl_loader_texture_hdr.html
  53. 1 1
      examples/webgl_materials_video.html
  54. 6 7
      examples/webgl_points_dynamic.html
  55. 1 5
      examples/webgl_postprocessing_backgrounds.html
  56. 17 2
      examples/webgl_postprocessing_masking.html
  57. 1 5
      examples/webgl_postprocessing_smaa.html
  58. 4 4
      examples/webgl_postprocessing_sobel.html
  59. 1 5
      examples/webgl_postprocessing_ssaa.html
  60. 2 5
      examples/webgl_postprocessing_ssaa_unbiased.html
  61. 8 11
      examples/webgl_postprocessing_ssao.html
  62. 1 5
      examples/webgl_postprocessing_taa.html
  63. 0 1
      examples/webgl_postprocessing_unreal_bloom.html
  64. 2 4
      examples/webgl_postprocessing_unreal_bloom_selective.html
  65. 2 3
      examples/webgl_shader_lava.html
  66. 4 0
      files/ic_close_black_24dp.svg
  67. 4 0
      files/ic_code_black_24dp.svg
  68. 15 0
      files/ic_github_black_24dp.svg
  69. 165 85
      files/main.css
  70. 17 3
      src/animation/AnimationAction.js
  71. 11 0
      src/core/BufferAttribute.js
  72. 2 12
      src/core/BufferGeometry.js
  73. 12 0
      src/core/InstancedBufferAttribute.js
  74. 1 0
      src/geometries/ExtrudeGeometry.d.ts
  75. 3 0
      src/geometries/TextGeometry.d.ts
  76. 1 0
      src/materials/Material.d.ts
  77. 1 1
      src/renderers/WebGLRenderer.js
  78. 9 0
      src/renderers/webgl/WebGLAnimation.d.ts
  79. 9 0
      src/renderers/webgl/WebGLAttributes.d.ts
  80. 17 0
      src/renderers/webgl/WebGLBackground.d.ts
  81. 5 0
      src/renderers/webgl/WebGLUtils.d.ts
  82. 2 0
      src/renderers/webvr/WebVRManager.js
  83. 9 1
      utils/modularize.js

+ 50 - 37
build/three.js

@@ -21971,6 +21971,7 @@
 
 
 	function WebVRManager( renderer ) {
 	function WebVRManager( renderer ) {
 
 
+		var renderWidth, renderHeight;
 		var scope = this;
 		var scope = this;
 
 
 		var device = null;
 		var device = null;
@@ -21998,11 +21999,11 @@
 		var tempPosition = new Vector3();
 		var tempPosition = new Vector3();
 
 
 		var cameraL = new PerspectiveCamera();
 		var cameraL = new PerspectiveCamera();
-		cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );
+		cameraL.viewport = new Vector4();
 		cameraL.layers.enable( 1 );
 		cameraL.layers.enable( 1 );
 
 
 		var cameraR = new PerspectiveCamera();
 		var cameraR = new PerspectiveCamera();
-		cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );
+		cameraR.viewport = new Vector4();
 		cameraR.layers.enable( 2 );
 		cameraR.layers.enable( 2 );
 
 
 		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
 		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
@@ -22024,14 +22025,17 @@
 			if ( isPresenting() ) {
 			if ( isPresenting() ) {
 
 
 				var eyeParameters = device.getEyeParameters( 'left' );
 				var eyeParameters = device.getEyeParameters( 'left' );
-				var renderWidth = eyeParameters.renderWidth * framebufferScaleFactor;
-				var renderHeight = eyeParameters.renderHeight * framebufferScaleFactor;
+				renderWidth = eyeParameters.renderWidth * framebufferScaleFactor;
+				renderHeight = eyeParameters.renderHeight * framebufferScaleFactor;
 
 
 				currentPixelRatio = renderer.getPixelRatio();
 				currentPixelRatio = renderer.getPixelRatio();
 				renderer.getSize( currentSize );
 				renderer.getSize( currentSize );
 
 
 				renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 				renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 
 
+				cameraL.viewport.set( 0, 0, renderWidth, renderHeight );
+				cameraR.viewport.set( renderWidth, 0, renderWidth, renderHeight );
+
 				animation.start();
 				animation.start();
 
 
 			} else {
 			} else {
@@ -22132,6 +22136,16 @@
 
 
 		}
 		}
 
 
+		function updateViewportFromBounds( viewport, bounds ) {
+
+			if ( bounds !== null && bounds.length === 4 ) {
+
+				viewport.set( bounds[ 0 ] * renderWidth, bounds[ 1 ] * renderHeight, bounds[ 2 ] * renderWidth, bounds[ 3 ] * renderHeight );
+
+			}
+
+		}
+
 		//
 		//
 
 
 		this.enabled = false;
 		this.enabled = false;
@@ -22299,17 +22313,8 @@
 
 
 				var layer = layers[ 0 ];
 				var layer = layers[ 0 ];
 
 
-				if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) {
-
-					cameraL.bounds.fromArray( layer.leftBounds );
-
-				}
-
-				if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) {
-
-					cameraR.bounds.fromArray( layer.rightBounds );
-
-				}
+				updateViewportFromBounds( cameraL.viewport, layer.leftBounds );
+				updateViewportFromBounds( cameraR.viewport, layer.rightBounds );
 
 
 			}
 			}
 
 
@@ -22703,7 +22708,7 @@
 			 * Enables error checking and reporting when shader programs are being compiled
 			 * Enables error checking and reporting when shader programs are being compiled
 			 * @type {boolean}
 			 * @type {boolean}
 			 */
 			 */
-			checkShaderErrors: false
+			checkShaderErrors: true
 		};
 		};
 
 
 		// clearing
 		// clearing
@@ -23998,22 +24003,7 @@
 
 
 						if ( object.layers.test( camera2.layers ) ) {
 						if ( object.layers.test( camera2.layers ) ) {
 
 
-							if ( 'viewport' in camera2 ) { // XR
-
-								state.viewport( _currentViewport.copy( camera2.viewport ) );
-
-							} else {
-
-								var bounds = camera2.bounds;
-
-								var x = bounds.x * _width;
-								var y = bounds.y * _height;
-								var width = bounds.z * _width;
-								var height = bounds.w * _height;
-
-								state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-
-							}
+							state.viewport( _currentViewport.copy( camera2.viewport ) );
 
 
 							currentRenderState.setupLights( camera2 );
 							currentRenderState.setupLights( camera2 );
 
 
@@ -38035,8 +38025,17 @@
 
 
 			}
 			}
 
 
-			// Merges multi-byte utf-8 characters.
-			return decodeURIComponent( escape( s ) );
+			try {
+
+				// merges multi-byte utf-8 characters.
+
+				return decodeURIComponent( escape( s ) );
+
+			} catch ( e ) { // see #16358
+
+				return s;
+
+			}
 
 
 		},
 		},
 
 
@@ -43244,11 +43243,19 @@
 
 
 						time = 0;
 						time = 0;
 
 
-					} else break handle_stop;
+					} else {
+
+						this.time = time;
+
+						break handle_stop;
+
+					}
 
 
 					if ( this.clampWhenFinished ) this.paused = true;
 					if ( this.clampWhenFinished ) this.paused = true;
 					else this.enabled = false;
 					else this.enabled = false;
 
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						type: 'finished', action: this,
 						direction: deltaTime < 0 ? - 1 : 1
 						direction: deltaTime < 0 ? - 1 : 1
@@ -43300,6 +43307,8 @@
 
 
 						time = deltaTime > 0 ? duration : 0;
 						time = deltaTime > 0 ? duration : 0;
 
 
+						this.time = time;
+
 						this._mixer.dispatchEvent( {
 						this._mixer.dispatchEvent( {
 							type: 'finished', action: this,
 							type: 'finished', action: this,
 							direction: deltaTime > 0 ? 1 : - 1
 							direction: deltaTime > 0 ? 1 : - 1
@@ -43324,26 +43333,30 @@
 
 
 						this._loopCount = loopCount;
 						this._loopCount = loopCount;
 
 
+						this.time = time;
+
 						this._mixer.dispatchEvent( {
 						this._mixer.dispatchEvent( {
 							type: 'loop', action: this, loopDelta: loopDelta
 							type: 'loop', action: this, loopDelta: loopDelta
 						} );
 						} );
 
 
 					}
 					}
 
 
+				} else {
+
+					this.time = time;
+
 				}
 				}
 
 
 				if ( pingPong && ( loopCount & 1 ) === 1 ) {
 				if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 
 					// invert time for the "pong round"
 					// invert time for the "pong round"
 
 
-					this.time = time;
 					return duration - time;
 					return duration - time;
 
 
 				}
 				}
 
 
 			}
 			}
 
 
-			this.time = time;
 			return time;
 			return time;
 
 
 		},
 		},

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


+ 50 - 37
build/three.module.js

@@ -21965,6 +21965,7 @@ function setProjectionFromUnion( camera, cameraL, cameraR ) {
 
 
 function WebVRManager( renderer ) {
 function WebVRManager( renderer ) {
 
 
+	var renderWidth, renderHeight;
 	var scope = this;
 	var scope = this;
 
 
 	var device = null;
 	var device = null;
@@ -21992,11 +21993,11 @@ function WebVRManager( renderer ) {
 	var tempPosition = new Vector3();
 	var tempPosition = new Vector3();
 
 
 	var cameraL = new PerspectiveCamera();
 	var cameraL = new PerspectiveCamera();
-	cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );
+	cameraL.viewport = new Vector4();
 	cameraL.layers.enable( 1 );
 	cameraL.layers.enable( 1 );
 
 
 	var cameraR = new PerspectiveCamera();
 	var cameraR = new PerspectiveCamera();
-	cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );
+	cameraR.viewport = new Vector4();
 	cameraR.layers.enable( 2 );
 	cameraR.layers.enable( 2 );
 
 
 	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
 	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
@@ -22018,14 +22019,17 @@ function WebVRManager( renderer ) {
 		if ( isPresenting() ) {
 		if ( isPresenting() ) {
 
 
 			var eyeParameters = device.getEyeParameters( 'left' );
 			var eyeParameters = device.getEyeParameters( 'left' );
-			var renderWidth = eyeParameters.renderWidth * framebufferScaleFactor;
-			var renderHeight = eyeParameters.renderHeight * framebufferScaleFactor;
+			renderWidth = eyeParameters.renderWidth * framebufferScaleFactor;
+			renderHeight = eyeParameters.renderHeight * framebufferScaleFactor;
 
 
 			currentPixelRatio = renderer.getPixelRatio();
 			currentPixelRatio = renderer.getPixelRatio();
 			renderer.getSize( currentSize );
 			renderer.getSize( currentSize );
 
 
 			renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 			renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 
 
+			cameraL.viewport.set( 0, 0, renderWidth, renderHeight );
+			cameraR.viewport.set( renderWidth, 0, renderWidth, renderHeight );
+
 			animation.start();
 			animation.start();
 
 
 		} else {
 		} else {
@@ -22126,6 +22130,16 @@ function WebVRManager( renderer ) {
 
 
 	}
 	}
 
 
+	function updateViewportFromBounds( viewport, bounds ) {
+
+		if ( bounds !== null && bounds.length === 4 ) {
+
+			viewport.set( bounds[ 0 ] * renderWidth, bounds[ 1 ] * renderHeight, bounds[ 2 ] * renderWidth, bounds[ 3 ] * renderHeight );
+
+		}
+
+	}
+
 	//
 	//
 
 
 	this.enabled = false;
 	this.enabled = false;
@@ -22293,17 +22307,8 @@ function WebVRManager( renderer ) {
 
 
 			var layer = layers[ 0 ];
 			var layer = layers[ 0 ];
 
 
-			if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) {
-
-				cameraL.bounds.fromArray( layer.leftBounds );
-
-			}
-
-			if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) {
-
-				cameraR.bounds.fromArray( layer.rightBounds );
-
-			}
+			updateViewportFromBounds( cameraL.viewport, layer.leftBounds );
+			updateViewportFromBounds( cameraR.viewport, layer.rightBounds );
 
 
 		}
 		}
 
 
@@ -22697,7 +22702,7 @@ function WebGLRenderer( parameters ) {
 		 * Enables error checking and reporting when shader programs are being compiled
 		 * Enables error checking and reporting when shader programs are being compiled
 		 * @type {boolean}
 		 * @type {boolean}
 		 */
 		 */
-		checkShaderErrors: false
+		checkShaderErrors: true
 	};
 	};
 
 
 	// clearing
 	// clearing
@@ -23992,22 +23997,7 @@ function WebGLRenderer( parameters ) {
 
 
 					if ( object.layers.test( camera2.layers ) ) {
 					if ( object.layers.test( camera2.layers ) ) {
 
 
-						if ( 'viewport' in camera2 ) { // XR
-
-							state.viewport( _currentViewport.copy( camera2.viewport ) );
-
-						} else {
-
-							var bounds = camera2.bounds;
-
-							var x = bounds.x * _width;
-							var y = bounds.y * _height;
-							var width = bounds.z * _width;
-							var height = bounds.w * _height;
-
-							state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-
-						}
+						state.viewport( _currentViewport.copy( camera2.viewport ) );
 
 
 						currentRenderState.setupLights( camera2 );
 						currentRenderState.setupLights( camera2 );
 
 
@@ -38029,8 +38019,17 @@ var LoaderUtils = {
 
 
 		}
 		}
 
 
-		// Merges multi-byte utf-8 characters.
-		return decodeURIComponent( escape( s ) );
+		try {
+
+			// merges multi-byte utf-8 characters.
+
+			return decodeURIComponent( escape( s ) );
+
+		} catch ( e ) { // see #16358
+
+			return s;
+
+		}
 
 
 	},
 	},
 
 
@@ -43238,11 +43237,19 @@ Object.assign( AnimationAction.prototype, {
 
 
 					time = 0;
 					time = 0;
 
 
-				} else break handle_stop;
+				} else {
+
+					this.time = time;
+
+					break handle_stop;
+
+				}
 
 
 				if ( this.clampWhenFinished ) this.paused = true;
 				if ( this.clampWhenFinished ) this.paused = true;
 				else this.enabled = false;
 				else this.enabled = false;
 
 
+				this.time = time;
+
 				this._mixer.dispatchEvent( {
 				this._mixer.dispatchEvent( {
 					type: 'finished', action: this,
 					type: 'finished', action: this,
 					direction: deltaTime < 0 ? - 1 : 1
 					direction: deltaTime < 0 ? - 1 : 1
@@ -43294,6 +43301,8 @@ Object.assign( AnimationAction.prototype, {
 
 
 					time = deltaTime > 0 ? duration : 0;
 					time = deltaTime > 0 ? duration : 0;
 
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						type: 'finished', action: this,
 						direction: deltaTime > 0 ? 1 : - 1
 						direction: deltaTime > 0 ? 1 : - 1
@@ -43318,26 +43327,30 @@ Object.assign( AnimationAction.prototype, {
 
 
 					this._loopCount = loopCount;
 					this._loopCount = loopCount;
 
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 					this._mixer.dispatchEvent( {
 						type: 'loop', action: this, loopDelta: loopDelta
 						type: 'loop', action: this, loopDelta: loopDelta
 					} );
 					} );
 
 
 				}
 				}
 
 
+			} else {
+
+				this.time = time;
+
 			}
 			}
 
 
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 
 				// invert time for the "pong round"
 				// invert time for the "pong round"
 
 
-				this.time = time;
 				return duration - time;
 				return duration - time;
 
 
 			}
 			}
 
 
 		}
 		}
 
 
-		this.time = time;
 		return time;
 		return time;
 
 
 	},
 	},

+ 1 - 1
docs/api/en/renderers/WebGLRenderer.html

@@ -425,7 +425,7 @@
 
 
 		<h3>[method:null setAnimationLoop]( [param:Function callback] )</h3>
 		<h3>[method:null setAnimationLoop]( [param:Function callback] )</h3>
 		<p>[page:Function callback] — The function will be called every available frame. If `null` is passed it will stop any already ongoing animation.</p>
 		<p>[page:Function callback] — The function will be called every available frame. If `null` is passed it will stop any already ongoing animation.</p>
-		<p>A build in function that can be used instead of [link:https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame requestAnimationFrame]. For WebVR projects this function must be used.</p>
+		<p>A built in function that can be used instead of [link:https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame requestAnimationFrame]. For WebVR projects this function must be used.</p>
 
 
 		<h3>[method:null setClearAlpha]( [param:Float alpha] )</h3>
 		<h3>[method:null setClearAlpha]( [param:Float alpha] )</h3>
 		<p>Sets the clear alpha. Valid input is a float between *0.0* and *1.0*.</p>
 		<p>Sets the clear alpha. Valid input is a float between *0.0* and *1.0*.</p>

+ 53 - 11
docs/index.html

@@ -10,30 +10,33 @@
 		<script src="../build/three.min.js" async defer></script>
 		<script src="../build/three.min.js" async defer></script>
 	</head>
 	</head>
 	<body>
 	<body>
-
-		<div id="panel" class="collapsed">
+		<div id="panel" class="">
 
 
 			<div id="header">
 			<div id="header">
-
 				<h1><a href="http://threejs.org">three.js</a></h1>
 				<h1><a href="http://threejs.org">three.js</a></h1>
 
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
-
 				<div id="sections">
 				<div id="sections">
-					<span class="selected">docs</span> <a href="../examples/">examples</a>
+					<span class="selected">docs</span>
+					<a href="../examples/">examples</a>
 				</div>
 				</div>
 
 
-				<input type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false">
+				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
+			</div>
+
+			<div id="panelScrim"></div>
+
+			<div id="contentWrapper">
+				<input placeholder="Search" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
+				<div id="exitSearchButton"></div>
 
 
 				<select id="language">
 				<select id="language">
 					<option value="en">en</option>
 					<option value="en">en</option>
 					<option value="zh">zh</option>
 					<option value="zh">zh</option>
 				</select>
 				</select>
 
 
+				<div id="content"></div>
 			</div>
 			</div>
 
 
-			<div id="content"></div>
-
 		</div>
 		</div>
 
 
 		<iframe name="viewer"></iframe>
 		<iframe name="viewer"></iframe>
@@ -89,6 +92,8 @@
 			var panel = document.getElementById( 'panel' );
 			var panel = document.getElementById( 'panel' );
 			var content = document.getElementById( 'content' );
 			var content = document.getElementById( 'content' );
 			var expandButton = document.getElementById( 'expandButton' );
 			var expandButton = document.getElementById( 'expandButton' );
+			var exitSearchButton = document.getElementById( 'exitSearchButton' );
+			var panelScrim = document.getElementById( 'panelScrim' );
 			var filterInput = document.getElementById( 'filter' );
 			var filterInput = document.getElementById( 'filter' );
 			var iframe = document.querySelector( 'iframe' );
 			var iframe = document.querySelector( 'iframe' );
 
 
@@ -104,12 +109,40 @@
 			expandButton.onclick = function ( event ) {
 			expandButton.onclick = function ( event ) {
 
 
 				event.preventDefault();
 				event.preventDefault();
-				panel.classList.toggle( 'collapsed' );
+				panel.classList.toggle( 'open' );
+
+			};
+
+			panelScrim.onclick = function ( event ) {
+
+				event.preventDefault();
+				panel.classList.toggle( 'open' );
 
 
 			};
 			};
 
 
 
 
 			// Functionality for search/filter input field
 			// Functionality for search/filter input field
+			filterInput.onfocus = function ( event ) {
+
+				panel.classList.add('searchFocused');
+
+			}
+
+			filterInput.onblur = function ( event ) {
+
+				if(filterInput.value === '') {
+					panel.classList.remove('searchFocused');
+				}
+
+			}
+
+			exitSearchButton.onclick = function( event ) {
+
+				filterInput.value = '';
+				updateFilter();
+				panel.classList.remove('searchFocused');
+
+			}
 
 
 			filterInput.oninput = function ( event ) {
 			filterInput.oninput = function ( event ) {
 
 
@@ -139,7 +172,16 @@
 					if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
 					if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
 
 
 					window.location.hash = pageURL;
 					window.location.hash = pageURL;
-					panel.classList.add( 'collapsed' );
+					panel.classList.remove( 'open' );
+
+
+					content.querySelectorAll( 'a' ).forEach( function ( item ) {
+
+						item.classList.remove( 'selected' );
+
+					} );
+
+					link.classList.add('selected');
 
 
 				} );
 				} );
 
 

+ 10 - 2
docs/manual/en/introduction/Import-via-modules.html

@@ -27,7 +27,7 @@
 
 
 		<h2>Importing the module</h2>
 		<h2>Importing the module</h2>
 
 
-		<p>Assuming that you're bundling your files with a tool such as [link:https://webpack.github.io/ Webpack] or [link:https://github.com/substack/node-browserify Browserify], which allow you to "require('modules') in the browser by bundling up all of your dependencies."</p>
+		<p>Assuming that you're bundling your files with a tool such as [link:https://webpack.github.io/ Webpack] or [link:https://github.com/substack/node-browserify Browserify], which allow you to "require('modules')" in the browser by bundling up all of your dependencies.</p>
 
 
 		<p>
 		<p>
 			You should now be able to import the module into your source files and continue to use it as per normal.
 			You should now be able to import the module into your source files and continue to use it as per normal.
@@ -78,8 +78,12 @@
 			<ul>
 			<ul>
 				<li>controls
 				<li>controls
 					<ul>
 					<ul>
+						<li>DeviceOrientationControls</li>
+						<li>DragControls</li>
+						<li>EditorControls</li>
 						<li>MapControls</li>
 						<li>MapControls</li>
 						<li>OrbitControls</li>
 						<li>OrbitControls</li>
+						<li>PointerLockControls</li>
 						<li>TrackballControls</li>
 						<li>TrackballControls</li>
 					</ul>
 					</ul>
 				</li>
 				</li>
@@ -96,10 +100,14 @@
 				</li>
 				</li>
 				<li>loaders
 				<li>loaders
 					<ul>
 					<ul>
+						<li>BVHLoader</li>
 						<li>GLTFLoader</li>
 						<li>GLTFLoader</li>
 						<li>MTLLoader</li>
 						<li>MTLLoader</li>
 						<li>OBJLoader</li>
 						<li>OBJLoader</li>
+						<li>PCDLoader</li>
+						<li>PLYLoader</li>
 						<li>STLLoader</li>
 						<li>STLLoader</li>
+						<li>TGALoader</li>
 					</ul>
 					</ul>
 				</li>
 				</li>
 				<li>pmrem
 				<li>pmrem
@@ -124,7 +132,7 @@
 		</p>
 		</p>
 		<p>
 		<p>
 			Note: When using code from the examples directory, it's important that all files match the version of
 			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
+			your three.js main file. For example, it's not acceptable 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
 			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.
 			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.
 			In both cases, ensure the code is compatible with your three.js main file.

+ 15 - 13
docs/manual/en/introduction/Useful-links.html

@@ -25,7 +25,7 @@
 
 
 		<h2>Help forums</h2>
 		<h2>Help forums</h2>
 		<p>
 		<p>
-			Three.js officially uses [link:http://stackoverflow.com/tags/three.js/info Stack Overflow] for help requests.
+			Three.js officially uses the [link:https://discourse.threejs.org/ forum] and [link:http://stackoverflow.com/tags/three.js/info Stack Overflow] for help requests.
 			If you need assistance with something, that's the place to go. Do NOT open an issue on Github for help requests.
 			If you need assistance with something, that's the place to go. Do NOT open an issue on Github for help requests.
 		</p>
 		</p>
 
 
@@ -46,6 +46,9 @@
 
 
 		<h3>More extensive / advanced articles and courses</h3>
 		<h3>More extensive / advanced articles and courses</h3>
 		<ul>
 		<ul>
+			<li>
+				[link:https://discoverthreejs.com/ Discover three.js]
+			</li>
 			<li>
 			<li>
 				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
 				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
 			</li>
 			</li>
@@ -66,21 +69,16 @@
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 		 </li>
 		 </li>
 		 <li>
 		 <li>
-			 [link:http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html Animated selective glow in Three.js]
-			 by [link:https://github.com/BKcore BKcore]
+			 [link:https://discourse.threejs.org/t/three-js-bookshelf/2468 Three.js Bookshelf] - Looking for more resources about three.js or computer graphics in general?
+			 Check out the selection of literature recommended by the community.
 		 </li>
 		 </li>
 		</ul>
 		</ul>
 
 
-		<h3>Tutorials in other languages</h3>
+		<h2>News and Updates</h2>
 		<ul>
 		<ul>
 			<li>
 			<li>
-				[link:http://www.natural-science.or.jp/article/20120220155529.php Building A Physics Simulation Environment] - three.js tutorial in Japanese
+				[link:https://twitter.com/hashtag/threejs Three.js on Twitter]
 			</li>
 			</li>
-
-		</ul>
-
-		<h2>News and Updates</h2>
-		<ul>
 			<li>
 			<li>
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 			</li>
 			</li>
@@ -90,9 +88,6 @@
 			<li>
 			<li>
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 			</li>
 			</li>
-			<li>
-				[link:https://plus.google.com/104300307601542851567/posts Three.js posts] on Google+ – frequent posts on Three.js
-			</li>
 		</ul>
 		</ul>
 
 
 		<h2>Examples</h2>
 		<h2>Examples</h2>
@@ -179,6 +174,13 @@
 			[link:http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/ A whirlwind look at Three.js]
 			[link:http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/ A whirlwind look at Three.js]
 			by [link:http://github.com/nrocy Paul King]
 			by [link:http://github.com/nrocy Paul King]
 		</li>
 		</li>
+		<li>
+			[link:http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html Animated selective glow in Three.js]
+			by [link:https://github.com/BKcore BKcore]
+		</li>
+		<li>
+			[link:http://www.natural-science.or.jp/article/20120220155529.php Building A Physics Simulation Environment] - three.js tutorial in Japanese
+		</li>
 	 </ul>
 	 </ul>
 
 
 	</body>
 	</body>

+ 20 - 12
docs/manual/zh/introduction/Useful-links.html

@@ -31,6 +31,9 @@
 
 
 		<h3>three.js入门</h3>
 		<h3>three.js入门</h3>
 		<ul>
 		<ul>
+			<li>
+				[link:https://threejsfundamentals.org/threejs/lessons/threejs-fundamentals.html Three.js Fundamentals starting lesson]
+			</li>
 			<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].
 				[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>
 			</li>
@@ -41,6 +44,12 @@
 
 
 		<h3>更加广泛、高级的文章与教程</h3>
 		<h3>更加广泛、高级的文章与教程</h3>
 		<ul>
 		<ul>
+			<li>
+				[link:https://discoverthreejs.com/ Discover three.js]
+			</li>
+			<li>
+				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
+			</li>
 			<li>
 			<li>
 				[link:http://blog.cjgammon.com/ Collection of tutorials] by [link:http://www.cjgammon.com/ CJ Gammon].
 				[link:http://blog.cjgammon.com/ Collection of tutorials] by [link:http://www.cjgammon.com/ CJ Gammon].
 			</li>
 			</li>
@@ -58,21 +67,16 @@
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 		 </li>
 		 </li>
 		 <li>
 		 <li>
-			 [link:http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html Animated selective glow in Three.js]
-			 by [link:https://github.com/BKcore BKcore]
+			 [link:https://discourse.threejs.org/t/three-js-bookshelf/2468 Three.js Bookshelf] - Looking for more resources about three.js or computer graphics in general?
+			 Check out the selection of literature recommended by the community.
 		 </li>
 		 </li>
 		</ul>
 		</ul>
 
 
-		<h3>其它非英语的教程</h3>
+		<h2>新闻与更新</h2>
 		<ul>
 		<ul>
 			<li>
 			<li>
-				[link:http://www.natural-science.or.jp/article/20120220155529.php Building A Physics Simulation Environment] - three.js tutorial in Japanese
+				[link:https://twitter.com/hashtag/threejs Three.js on Twitter]
 			</li>
 			</li>
-
-		</ul>
-
-		<h2>新闻与更新</h2>
-		<ul>
 			<li>
 			<li>
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 			</li>
 			</li>
@@ -82,9 +86,6 @@
 			<li>
 			<li>
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 			</li>
 			</li>
-			<li>
-				[link:https://plus.google.com/104300307601542851567/posts Three.js posts] on Google+ – frequent posts on Three.js
-			</li>
 		</ul>
 		</ul>
 		<h2>示例</h2>
 		<h2>示例</h2>
 		<ul>
 		<ul>
@@ -165,6 +166,13 @@
 			[link:http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/ A whirlwind look at Three.js]
 			[link:http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/ A whirlwind look at Three.js]
 			by [link:http://github.com/nrocy Paul King]
 			by [link:http://github.com/nrocy Paul King]
 		</li>
 		</li>
+		<li>
+			[link:http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html Animated selective glow in Three.js]
+			by [link:https://github.com/BKcore BKcore]
+		</li>
+		<li>
+			[link:http://www.natural-science.or.jp/article/20120220155529.php Building A Physics Simulation Environment] - three.js tutorial in Japanese
+		</li>
 	 </ul>
 	 </ul>
 
 
 	</body>
 	</body>

+ 89 - 38
docs/page.css

@@ -1,6 +1,13 @@
+:root {
+	--color-blue: #049EF4;
+	--text-color: #444;
+	--border-style: 1px solid #EEE;
+	--header-height: 56px;
+}
+
 @font-face {
 @font-face {
-	font-family: 'RobotoMono';
-	src: local('RobotoMono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
+	font-family: 'Roboto Mono';
+	src: local('Roboto Mono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
 	font-weight: normal;
 	font-weight: normal;
 	font-style: normal;
 	font-style: normal;
 }
 }
@@ -13,54 +20,86 @@
 }
 }
 
 
 body {
 body {
-	margin: 78px auto;
-	padding: 0px 24px;
-	max-width: 780px;
-	color: #555;
+	padding: 20px 24px 40px 24px;
+	margin: 0;
+	color: var(--text-color);
 	font-family: 'SF-Pro-Text', sans-serif;
 	font-family: 'SF-Pro-Text', sans-serif;
 	font-size: 16px;
 	font-size: 16px;
-	line-height: 23px;
+	line-height: 24px;
 	tab-size: 4;
 	tab-size: 4;
 	overflow: auto;
 	overflow: auto;
 }
 }
 
 
 a {
 a {
-	color: #1184CE;
+	color: var(--color-blue);
 	cursor: pointer;
 	cursor: pointer;
-	text-decoration: underline;
+	text-decoration: none;
 }
 }
 
 
 h1 {
 h1 {
-	color: #049EF4;
-	font-size: 32px;
+	color: var(--color-blue);
+	font-size: 2.4em;
 	font-weight: normal;
 	font-weight: normal;
-	line-height: 42px;
+	line-height: 1.36em;
+	margin-top: 16px;
+	margin-bottom: -16px;
+	text-indent: -2px;
 }
 }
 
 
 h2 {
 h2 {
-	color: #4B0;
-
-	font-size: 22px;
+	color: var(--color-blue);
+	font-size: 1.8em;
+	line-height: 1.32em;
 	font-weight: normal;
 	font-weight: normal;
-	line-height: 31px;
+	margin-top: 40px;
+	margin-bottom: 12px;
+	text-indent: -1px;
 }
 }
 
 
 h3 {
 h3 {
-	color: #000;
-	font-size: 16px;
+	font-size: 1.32em;
+	line-height: 1.48em;
 	font-weight: normal;
 	font-weight: normal;
+	text-indent: -1px;
+	margin-top: 24px;
+	margin-bottom: 12px;
+}
 
 
-	margin-top: 40px;
+p {
+	margin-top: 24px;
+	margin-bottom: 24px;
+}
+ul, ol {
+	box-sizing: border-box;
+	padding-left: 20px;
+}
+ul li,
+ol li {
+	padding-left: 4px;
+	margin-bottom: 4px;
+}
+
+li ul,
+li ol {
+	margin-top: 4px;
 }
 }
 
 
-p, ul, ol {
-	margin-top: 0;
+body {
+	max-width: 760px;
+	margin-left: auto;
+	margin-right: auto;
+}
+
+table,
+pre,
+code {
+	margin-left: -24px;
+	margin-right: -24px;
+	margin-top: 20px;
 	margin-bottom: 20px;
 	margin-bottom: 20px;
-	max-width: 780px;
 }
 }
 
 
 div {
 div {
-	/* padding-left: 30px; */
 	margin-bottom: 20px;
 	margin-bottom: 20px;
 }
 }
 
 
@@ -68,17 +107,30 @@ div {
 	padding-left: 0px;
 	padding-left: 0px;
 }
 }
 
 
-pre, code {
-	margin-top: 20px;
-	margin-bottom: 20px;
+br {
+	display: none;
+}
+
+table {
+	border-spacing: 24px 4px;
+}
+table,
+table tr,
+table td {
+	text-align: left;
+}
+
+table th {
+	text-decoration: none;
+	padding: 2px 0;
 }
 }
 
 
 code {
 code {
 	display: block;
 	display: block;
-	padding: 20px;
+	padding: 20px 24px;
 	white-space: pre-wrap;
 	white-space: pre-wrap;
-	background-color: #f9f9f9;
 	overflow: auto;
 	overflow: auto;
+	box-sizing: border-box;
 }
 }
 
 
 iframe {
 iframe {
@@ -109,15 +161,15 @@ strong {
 
 
 #button {
 #button {
 	position: fixed;
 	position: fixed;
-	bottom: 16px;
-	right: 16px;
+	bottom: 12px;
+	right: 12px;
 
 
 	padding: 8px;
 	padding: 8px;
 	border-radius: 50%;
 	border-radius: 50%;
-	margin-bottom: 0px; /* TODO div sets it to 20px */
+	margin-bottom: 0px;
 
 
-	background-color: #dddddd;
-	opacity: 0.4;
+	background-color: #E5E5E5;
+	opacity: .9;
 }
 }
 
 
 	#button:hover {
 	#button:hover {
@@ -127,6 +179,7 @@ strong {
 
 
 	#button img {
 	#button img {
 		display: block;
 		display: block;
+		width: 20px;
 	}
 	}
 
 
 a.permalink {
 a.permalink {
@@ -157,19 +210,17 @@ sub {
 /* mobile */
 /* mobile */
 
 
 @media all and ( max-width: 640px ) {
 @media all and ( max-width: 640px ) {
-
 	body {
 	body {
-		margin: 14px auto;
-		padding: 0px 14px;
-		font-size: 14px;
-		line-height: 22px;
+		padding: 16px 20px;
 	}
 	}
 
 
 	h1 {
 	h1 {
+		margin-top: 0;
 		font-size: 26px;
 		font-size: 26px;
 	}
 	}
 
 
 	h2 {
 	h2 {
+		margin-top: 20px;
 		font-size: 18px;
 		font-size: 18px;
 		line-height: 25px;
 		line-height: 25px;
 	}
 	}

+ 2 - 2
docs/prettify/threejs.css

@@ -11,7 +11,7 @@ pre .dec, code .dec { color: #22c0c4; } /* decimal */
 
 
 pre.prettyprint, code.prettyprint {
 pre.prettyprint, code.prettyprint {
 	background-color: #F5F5F5;
 	background-color: #F5F5F5;
-	font-family: 'RobotoMono', monospace;
+	font-family: 'Roboto Mono', monospace;
 	font-size: 14px;
 	font-size: 14px;
-	line-height: 21px;
+	line-height: 24px;
 }
 }

+ 5 - 4
editor/manifest.json

@@ -1,13 +1,14 @@
 {
 {
-  "short_name": "Editor",
   "name": "Three.js Editor",
   "name": "Three.js Editor",
+  "short_name": "Three.js",
+  "start_url": ".",
+  "scope": ".",
+  "display": "standalone",
   "icons": [
   "icons": [
     {
     {
       "src": "./images/icon.png",
       "src": "./images/icon.png",
       "type": "image/png",
       "type": "image/png",
       "sizes": "144x144"
       "sizes": "144x144"
     }
     }
-  ],
-  "start_url": ".",
-  "display": "standalone"
+  ]
 }
 }

+ 2 - 2
examples/css3d_panorama_deviceorientation.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html>
 <html>
 	<head>
 	<head>
-		<title>three.js css3d - panorama - deviceorientation</title>
+		<title>three.js css3d - panorama - device orientation</title>
 		<meta charset="utf-8">
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 		<style>
@@ -34,7 +34,7 @@
 		<script src="js/controls/DeviceOrientationControls.js"></script>
 		<script src="js/controls/DeviceOrientationControls.js"></script>
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 		<script src="js/renderers/CSS3DRenderer.js"></script>
 
 
-		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - panorama - decideorientation. cubemap by <a href="http://www.humus.name/index.php?page=Textures" target="_blank" rel="noopener">Humus</a>.</div>
+		<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js css3d</a> - panorama - device orientation. cubemap by <a href="http://www.humus.name/index.php?page=Textures" target="_blank" rel="noopener">Humus</a>.</div>
 
 
 		<script>
 		<script>
 
 

+ 1 - 0
examples/files.js

@@ -75,6 +75,7 @@ var files = {
 		"webgl_lines_sphere",
 		"webgl_lines_sphere",
 		"webgl_loader_3ds",
 		"webgl_loader_3ds",
 		"webgl_loader_3mf",
 		"webgl_loader_3mf",
+		"webgl_loader_3mf_materials",
 		"webgl_loader_amf",
 		"webgl_loader_amf",
 		"webgl_loader_assimp",
 		"webgl_loader_assimp",
 		"webgl_loader_assimp2json",
 		"webgl_loader_assimp2json",

+ 66 - 24
examples/index.html

@@ -9,24 +9,29 @@
 		<style>
 		<style>
 			#panel #content .link {
 			#panel #content .link {
 				display: block;
 				display: block;
-				text-decoration: none;
-				cursor: pointer;
 			}
 			}
-
-			#viewSrcButton {
+			#button {
 				position: fixed;
 				position: fixed;
-				bottom: 20px;
-				right: 20px;
+				bottom: 12px;
+				right: 12px;
+
 				padding: 8px;
 				padding: 8px;
-				color: #fff;
-				background-color: #555;
-				opacity: 0.7;
+				border-radius: 50%;
+				margin-bottom: 0px; /* TODO div sets it to 20px */
+
+				background-color: #E5E5E5;
+				opacity: .9;
 			}
 			}
 
 
-			#viewSrcButton:hover {
+			#button:hover {
 				cursor: pointer;
 				cursor: pointer;
 				opacity: 1;
 				opacity: 1;
 			}
 			}
+
+			#button img {
+				display: block;
+				width: 20px;
+			}
 		</style>
 		</style>
 	</head>
 	</head>
 	<body>
 	<body>
@@ -34,25 +39,36 @@
 		<div id="panel">
 		<div id="panel">
 
 
 			<div id="header">
 			<div id="header">
-
 				<h1><a href="http://threejs.org">three.js</a></h1>
 				<h1><a href="http://threejs.org">three.js</a></h1>
 
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
-
 				<div id="sections">
 				<div id="sections">
-					<a href="../docs/">docs</a> <span class="selected">examples</span>
+					<a href="../docs/">docs</a>
+					<span class="selected">examples</span>
 				</div>
 				</div>
 
 
-				<input type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false"/>
-
+				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
 			</div>
 			</div>
 
 
-			<div id="content"></div>
+			<div id="panelScrim"></div>
+
+			<div id="contentWrapper">
+				<input placeholder="Search" type="text" id="filter" autocorrect="off" autocapitalize="off" spellcheck="false" />
+				<div id="exitSearchButton"></div>
+
+				<select id="language">
+					<option value="en">en</option>
+					<option value="zh">zh</option>
+				</select>
+
+				<div id="content"></div>
+			</div>
 
 
 		</div>
 		</div>
 
 
 		<iframe id="viewer" name="viewer" allowfullscreen allowvr onmousewheel=""></iframe>
 		<iframe id="viewer" name="viewer" allowfullscreen allowvr onmousewheel=""></iframe>
 
 
+		<a id="button" target="_blank"><img src="../files/ic_code_black_24dp.svg"></a>
+
 		<script src="files.js"></script>
 		<script src="files.js"></script>
 
 
 		<script>
 		<script>
@@ -70,13 +86,22 @@
 		var viewer = document.getElementById( 'viewer' );
 		var viewer = document.getElementById( 'viewer' );
 
 
 		var filterInput = document.getElementById( 'filter' );
 		var filterInput = document.getElementById( 'filter' );
+		var exitSearchButton = document.getElementById( 'exitSearchButton' );
 
 
 		var expandButton = document.getElementById( 'expandButton' );
 		var expandButton = document.getElementById( 'expandButton' );
 		expandButton.addEventListener( 'click', function ( event ) {
 		expandButton.addEventListener( 'click', function ( event ) {
 			event.preventDefault();
 			event.preventDefault();
-			panel.classList.toggle( 'collapsed' );
+			panel.classList.toggle( 'open' );
 		} );
 		} );
 
 
+		var panelScrim = document.getElementById( 'panelScrim' );
+		panelScrim.onclick = function ( event ) {
+
+			event.preventDefault();
+			panel.classList.toggle( 'open' );
+
+		};
+
 		// iOS iframe auto-resize workaround
 		// iOS iframe auto-resize workaround
 
 
 		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
 		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
@@ -90,12 +115,8 @@
 		var container = document.createElement( 'div' );
 		var container = document.createElement( 'div' );
 		content.appendChild( container );
 		content.appendChild( container );
 
 
-		var viewSrcButton = document.createElement( 'a' );
-		viewSrcButton.id = 'viewSrcButton';
-		viewSrcButton.target = '_blank';
-		viewSrcButton.textContent = 'View source';
+		var viewSrcButton = document.getElementById( 'button' );
 		viewSrcButton.style.display = 'none';
 		viewSrcButton.style.display = 'none';
-		document.body.appendChild( viewSrcButton );
 
 
 		var links = {};
 		var links = {};
 		var selected = null;
 		var selected = null;
@@ -157,7 +178,7 @@
 			window.location.hash = file;
 			window.location.hash = file;
 			viewer.focus();
 			viewer.focus();
 
 
-			panel.classList.add( 'collapsed' );
+			panel.classList.remove( 'open' );
 
 
 			selected = file;
 			selected = file;
 
 
@@ -175,6 +196,27 @@
 		}
 		}
 
 
 		// filter
 		// filter
+		filterInput.onfocus = function ( event ) {
+
+			panel.classList.add('searchFocused');
+
+		}
+
+		filterInput.onblur = function ( event ) {
+
+			if(filterInput.value === '') {
+				panel.classList.remove('searchFocused');
+			}
+
+		}
+
+		exitSearchButton.onclick = function( event ) {
+
+			filterInput.value = '';
+			updateFilter();
+			panel.classList.remove('searchFocused');
+
+		}
 
 
 		filterInput.addEventListener( 'input', function( e ) {
 		filterInput.addEventListener( 'input', function( e ) {
 
 

+ 6 - 1
examples/js/exporters/GLTFExporter.js

@@ -1740,7 +1740,12 @@ THREE.GLTFExporter.prototype = {
 
 
 			} else {
 			} else {
 
 
-				object.updateMatrix();
+				if ( object.matrixAutoUpdate ) {
+
+					object.updateMatrix();
+
+				}
+
 				if ( ! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
 				if ( ! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
 
 
 					gltfNode.matrix = object.matrix.elements;
 					gltfNode.matrix = object.matrix.elements;

+ 362 - 47
examples/js/loaders/3MFLoader.js

@@ -1,5 +1,17 @@
 /**
 /**
  * @author technohippy / https://github.com/technohippy
  * @author technohippy / https://github.com/technohippy
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * 3D Manufacturing Format (3MF) specification: https://3mf.io/specification/
+ *
+ * The following features from the core specification are supported:
+ *
+ * - 3D Models
+ * - Object Resources (Meshes and Components)
+ * - Material Resources (Base Materials)
+ *
+ * 3MF Materials and Properties Extension (e.g. textures) are not yet supported.
+ *
  */
  */
 
 
 THREE.ThreeMFLoader = function ( manager ) {
 THREE.ThreeMFLoader = function ( manager ) {
@@ -63,7 +75,7 @@ THREE.ThreeMFLoader.prototype = {
 
 
 				if ( e instanceof ReferenceError ) {
 				if ( e instanceof ReferenceError ) {
 
 
-					console.error( 'THREE.ThreeMFLoader: jszip missing and file is compressed.' );
+					console.error( 'THREE.3MFLoader: jszip missing and file is compressed.' );
 					return null;
 					return null;
 
 
 				}
 				}
@@ -72,7 +84,7 @@ THREE.ThreeMFLoader.prototype = {
 
 
 			for ( file in zip.files ) {
 			for ( file in zip.files ) {
 
 
-				if ( file.match( /\.rels$/ ) ) {
+				if ( file.match( /\_rels\/.rels$/ ) ) {
 
 
 					relsName = file;
 					relsName = file;
 
 
@@ -110,7 +122,7 @@ THREE.ThreeMFLoader.prototype = {
 
 
 				if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
 				if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
 
 
-					console.error( 'THREE.ThreeMFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
+					console.error( 'THREE.3MFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
 
 
 				}
 				}
 
 
@@ -206,9 +218,38 @@ THREE.ThreeMFLoader.prototype = {
 		}
 		}
 
 
 		function parseBasematerialsNode( basematerialsNode ) {
 		function parseBasematerialsNode( basematerialsNode ) {
+
+			var basematerialsData = {
+				id: basematerialsNode.getAttribute( 'id' ), // required
+				basematerials: []
+			};
+
+			var basematerialNodes = basematerialsNode.querySelectorAll( 'base' );
+
+			for ( var i = 0; i < basematerialNodes.length; i ++ ) {
+
+				var basematerialNode = basematerialNodes[ i ];
+				var basematerialData = parseBasematerialNode( basematerialNode );
+				basematerialsData.basematerials.push( basematerialData );
+
+			}
+
+			return basematerialsData;
+
 		}
 		}
 
 
-		function parseMeshNode( meshNode, extensions ) {
+		function parseBasematerialNode( basematerialNode ) {
+
+			var basematerialData = {};
+
+			basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required
+			basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required
+
+			return basematerialData;
+
+		}
+
+		function parseMeshNode( meshNode ) {
 
 
 			var meshData = {};
 			var meshData = {};
 
 
@@ -300,6 +341,59 @@ THREE.ThreeMFLoader.prototype = {
 
 
 		function parseComponentsNode( componentsNode ) {
 		function parseComponentsNode( componentsNode ) {
 
 
+			var components = [];
+
+			var componentNodes = componentsNode.querySelectorAll( 'component' );
+
+			for ( var i = 0; i < componentNodes.length; i ++ ) {
+
+				var componentNode = componentNodes[ i ];
+				var componentData = parseComponentNode( componentNode );
+				components.push( componentData );
+
+			}
+
+			return components;
+
+		}
+
+		function parseComponentNode( componentNode ) {
+
+			var componentData = {};
+
+			componentData[ 'objectId' ] = componentNode.getAttribute( 'objectid' ); // required
+
+			var transform = componentNode.getAttribute( 'transform' );
+
+			if ( transform ) {
+
+				componentData[ 'transform' ] = parseTransform( transform );
+
+			}
+
+			return componentData;
+
+		}
+
+		function parseTransform( transform ) {
+
+			var t = [];
+			transform.split( ' ' ).forEach( function ( s ) {
+
+				t.push( parseFloat( s ) );
+
+			} );
+
+			var matrix = new THREE.Matrix4();
+			matrix.set(
+				t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
+				t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
+				t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
+				 0.0, 0.0, 0.0, 1.0
+			);
+
+			return matrix;
+
 		}
 		}
 
 
 		function parseObjectNode( objectNode ) {
 		function parseObjectNode( objectNode ) {
@@ -383,7 +477,7 @@ THREE.ThreeMFLoader.prototype = {
 
 
 			if ( basematerialsNode ) {
 			if ( basematerialsNode ) {
 
 
-				resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode );
+				resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode );
 
 
 			}
 			}
 
 
@@ -411,25 +505,13 @@ THREE.ThreeMFLoader.prototype = {
 
 
 				var itemNode = itemNodes[ i ];
 				var itemNode = itemNodes[ i ];
 				var buildItem = {
 				var buildItem = {
-					objectid: itemNode.getAttribute( 'objectid' )
+					objectId: itemNode.getAttribute( 'objectid' )
 				};
 				};
 				var transform = itemNode.getAttribute( 'transform' );
 				var transform = itemNode.getAttribute( 'transform' );
 
 
 				if ( transform ) {
 				if ( transform ) {
 
 
-					var t = [];
-					transform.split( ' ' ).forEach( function ( s ) {
-
-						t.push( parseFloat( s ) );
-
-					} );
-					var mat4 = new THREE.Matrix4();
-					buildItem[ 'transform' ] = mat4.set(
-						t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ],
-						t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ],
-						t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ],
-						 0.0, 0.0, 0.0, 1.0
-					);
+					buildItem[ 'transform' ] = parseTransform( transform );
 
 
 				}
 				}
 
 
@@ -472,40 +554,176 @@ THREE.ThreeMFLoader.prototype = {
 
 
 		}
 		}
 
 
-		function buildMesh( meshData, data3mf ) {
+		function buildMesh( meshData, objects, modelData, objectData ) {
+
+			// geometry
 
 
 			var geometry = new THREE.BufferGeometry();
 			var geometry = new THREE.BufferGeometry();
 			geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
 			geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
 			geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
 			geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
 
 
-			if ( meshData[ 'colors' ] ) {
+			// groups
+
+			var basematerialsData = modelData[ 'resources' ][ 'basematerials' ];
+			var triangleProperties = meshData[ 'triangleProperties' ];
+
+			var start = 0;
+			var count = 0;
+			var currentMaterialIndex = - 1;
 
 
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) );
+			for ( var i = 0, l = triangleProperties.length; i < l; i ++ ) {
+
+				var triangleProperty = triangleProperties[ i ];
+				var pid = triangleProperty.pid;
+
+				// only proceed if the triangle refers to a basematerials definition
+
+				if ( basematerialsData && ( basematerialsData.id === pid ) ) {
+
+					if ( currentMaterialIndex === - 1 ) currentMaterialIndex = triangleProperty.p1;
+
+					if ( currentMaterialIndex === triangleProperty.p1 ) {
+
+						count += 3; // primitves per triangle
+
+					} else {
+
+						geometry.addGroup( start, count, currentMaterialIndex );
+
+						start += count;
+						count = 3;
+						currentMaterialIndex = triangleProperty.p1;
+
+					}
+
+				}
 
 
 			}
 			}
 
 
+			if ( geometry.groups.length > 0 ) mergeGroups( geometry );
+
 			geometry.computeBoundingSphere();
 			geometry.computeBoundingSphere();
 
 
-			var materialOpts = {
-				flatShading: true
-			};
+			// material
 
 
-			if ( meshData[ 'colors' ] && 0 < meshData[ 'colors' ].length ) {
+			var material;
 
 
-				materialOpts[ 'vertexColors' ] = THREE.VertexColors;
+			// add material if an object-level definition is present
 
 
-			} else {
+			if ( basematerialsData && ( basematerialsData.id === objectData.pid ) ) {
+
+				var materialIndex = objectData.pindex;
+				var basematerialData = basematerialsData.basematerials[ materialIndex ];
 
 
-				materialOpts[ 'color' ] = 0xaaaaff;
+				material = getBuild( basematerialData, objects, modelData, objectData, buildBasematerial );
 
 
 			}
 			}
 
 
-			var material = new THREE.MeshPhongMaterial( materialOpts );
+			// add/overwrite material if definitions on triangles are present
+
+			if ( geometry.groups.length > 0 ) {
+
+				var groups = geometry.groups;
+				material = [];
+
+				for ( var i = 0, l = groups.length; i < l; i ++ ) {
+
+					var group = groups[ i ];
+					var basematerialData = basematerialsData.basematerials[ group.materialIndex ];
+
+					material.push( getBuild( basematerialData, objects, modelData, objectData, buildBasematerial ) );
+
+				}
+
+			}
+
+			// default material
+
+			if ( material === undefined ) material = new THREE.MeshPhongMaterial( { color: 0xaaaaff, flatShading: true } );
+
 			return new THREE.Mesh( geometry, material );
 			return new THREE.Mesh( geometry, material );
 
 
 		}
 		}
 
 
-		function applyExtensions( extensions, meshData, modelXml, data3mf ) {
+		function mergeGroups( geometry ) {
+
+			// sort by material index
+
+			var groups = geometry.groups.sort( function ( a, b ) {
+
+				if ( a.materialIndex !== b.materialIndex ) return a.materialIndex - b.materialIndex;
+
+				return a.start - b.start;
+
+			} );
+
+			// reorganize index buffer
+
+			var index = geometry.index;
+
+			var itemSize = index.itemSize;
+			var srcArray = index.array;
+
+			var targetOffset = 0;
+
+			var targetArray = new srcArray.constructor( srcArray.length );
+
+			for ( var i = 0; i < groups.length; i ++ ) {
+
+				var group = groups[ i ];
+
+				var groupLength = group.count * itemSize;
+				var groupStart = group.start * itemSize;
+
+				var sub = srcArray.subarray( groupStart, groupStart + groupLength );
+
+				targetArray.set( sub, targetOffset );
+
+				targetOffset += groupLength;
+
+			}
+
+			srcArray.set( targetArray );
+
+			// update groups
+
+			var start = 0;
+
+			for ( i = 0; i < groups.length; i ++ ) {
+
+				group = groups[ i ];
+
+				group.start = start;
+				start += group.count;
+
+			}
+
+			// merge groups
+
+			var lastGroup = groups[ 0 ];
+
+			geometry.groups = [ lastGroup ];
+
+			for ( i = 1; i < groups.length; i ++ ) {
+
+				group = groups[ i ];
+
+				if ( lastGroup.materialIndex === group.materialIndex ) {
+
+					lastGroup.count += group.count;
+
+				} else {
+
+					lastGroup = group;
+					geometry.groups.push( lastGroup );
+
+				}
+
+			}
+
+		}
+
+		function applyExtensions( extensions, meshData, modelXml ) {
 
 
 			if ( ! extensions ) {
 			if ( ! extensions ) {
 
 
@@ -543,38 +761,131 @@ THREE.ThreeMFLoader.prototype = {
 
 
 		}
 		}
 
 
-		function buildMeshes( data3mf ) {
+		function getBuild( data, objects, modelData, objectData, builder ) {
+
+			if ( data.build !== undefined ) return data.build;
+
+			data.build = builder( data, objects, modelData, objectData );
+
+			return data.build;
+
+		}
+
+		function buildBasematerial( materialData ) {
+
+			var material = new THREE.MeshPhongMaterial( { flatShading: true } );
+
+			material.name = materialData.name;
+
+			// displaycolor MUST be specified with a value of a 6 or 8 digit hexadecimal number, e.g. "#RRGGBB" or "#RRGGBBAA"
+
+			var displaycolor = materialData.displaycolor;
+
+			var color = displaycolor.substring( 0, 7 );
+			material.color.setStyle( color );
+			material.color.convertSRGBToLinear(); // displaycolor is in sRGB
+
+			// process alpha if set
+
+			if ( displaycolor.length === 9 ) {
+
+				material.opacity = parseInt( displaycolor.charAt( 7 ) + displaycolor.charAt( 8 ), 16 ) / 255;
+
+			}
+
+			return material;
+
+		}
+
+		function buildComposite( compositeData, objects, modelData ) {
+
+			var composite = new THREE.Group();
+
+			for ( var j = 0; j < compositeData.length; j ++ ) {
+
+				var component = compositeData[ j ];
+				var build = objects[ component.objectId ];
+
+				if ( build === undefined ) {
+
+					buildObject( component.objectId, objects, modelData );
+					build = objects[ component.objectId ];
+
+				}
+
+				var object3D = build.clone();
+
+				// apply component transfrom
+
+				var transform = component.transform;
+
+				if ( transform ) {
+
+					object3D.applyMatrix( transform );
+
+				}
+
+				composite.add( object3D );
+
+			}
+
+			return composite;
+
+		}
+
+		function buildObject( objectId, objects, modelData ) {
+
+			var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
+
+			if ( objectData[ 'mesh' ] ) {
+
+				var meshData = objectData[ 'mesh' ];
+
+				var extensions = modelData[ 'extensions' ];
+				var modelXml = modelData[ 'xml' ];
+
+				applyExtensions( extensions, meshData, modelXml );
+
+				objects[ objectData.id ] = getBuild( meshData, objects, modelData, objectData, buildMesh );
+
+			} else {
+
+				var compositeData = objectData[ 'components' ];
+
+				objects[ objectData.id ] = getBuild( compositeData, objects, modelData, objectData, buildComposite );
+
+			}
+
+		}
+
+		function buildObjects( data3mf ) {
 
 
 			var modelsData = data3mf.model;
 			var modelsData = data3mf.model;
-			var meshes = {};
+			var objects = {};
 			var modelsKeys = Object.keys( modelsData );
 			var modelsKeys = Object.keys( modelsData );
 
 
 			for ( var i = 0; i < modelsKeys.length; i ++ ) {
 			for ( var i = 0; i < modelsKeys.length; i ++ ) {
 
 
 				var modelsKey = modelsKeys[ i ];
 				var modelsKey = modelsKeys[ i ];
 				var modelData = modelsData[ modelsKey ];
 				var modelData = modelsData[ modelsKey ];
-				var modelXml = modelData[ 'xml' ];
-				var extensions = modelData[ 'extensions' ];
 
 
 				var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
 				var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
 
 
 				for ( var j = 0; j < objectIds.length; j ++ ) {
 				for ( var j = 0; j < objectIds.length; j ++ ) {
 
 
 					var objectId = objectIds[ j ];
 					var objectId = objectIds[ j ];
-					var objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
-					var meshData = objectData[ 'mesh' ];
-					applyExtensions( extensions, meshData, modelXml, data3mf );
-					meshes[ objectId ] = buildMesh( meshData, data3mf );
+
+					buildObject( objectId, objects, modelData );
 
 
 				}
 				}
 
 
 			}
 			}
 
 
-			return meshes;
+			return objects;
 
 
 		}
 		}
 
 
-		function build( meshes, refs, data3mf ) {
+		function build( objects, refs, data3mf ) {
 
 
 			var group = new THREE.Group();
 			var group = new THREE.Group();
 			var buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ];
 			var buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ];
@@ -582,15 +893,19 @@ THREE.ThreeMFLoader.prototype = {
 			for ( var i = 0; i < buildData.length; i ++ ) {
 			for ( var i = 0; i < buildData.length; i ++ ) {
 
 
 				var buildItem = buildData[ i ];
 				var buildItem = buildData[ i ];
-				var mesh = meshes[ buildItem[ 'objectid' ] ];
+				var object3D = objects[ buildItem[ 'objectId' ] ];
+
+				// apply transform
 
 
-				if ( buildItem[ 'transform' ] ) {
+				var transform = buildItem[ 'transform' ];
+
+				if ( transform ) {
 
 
-					mesh.geometry.applyMatrix( buildItem[ 'transform' ] );
+					object3D.applyMatrix( transform );
 
 
 				}
 				}
 
 
-				group.add( mesh );
+				group.add( object3D );
 
 
 			}
 			}
 
 
@@ -599,9 +914,9 @@ THREE.ThreeMFLoader.prototype = {
 		}
 		}
 
 
 		var data3mf = loadDocument( data );
 		var data3mf = loadDocument( data );
-		var meshes = buildMeshes( data3mf );
+		var objects = buildObjects( data3mf );
 
 
-		return build( meshes, data3mf[ 'rels' ], data3mf );
+		return build( objects, data3mf[ 'rels' ], data3mf );
 
 
 	},
 	},
 
 

+ 4 - 2
examples/js/loaders/GLTFLoader.js

@@ -841,7 +841,8 @@ THREE.GLTFLoader = ( function () {
 
 
 				for ( var i = 0, il = params.length; i < il; i ++ ) {
 				for ( var i = 0, il = params.length; i < il; i ++ ) {
 
 
-					target[ params[ i ] ] = source[ params[ i ] ];
+					var value = source[ params[ i ] ];
+					target[ params[ i ] ] = value.isColor ? value.clone() : value;
 
 
 				}
 				}
 
 
@@ -1299,7 +1300,7 @@ THREE.GLTFLoader = ( function () {
 
 
 			if ( typeof gltfDef.extras === 'object' ) {
 			if ( typeof gltfDef.extras === 'object' ) {
 
 
-				object.userData = gltfDef.extras;
+				Object.assign( object.userData, gltfDef.extras );
 
 
 			} else {
 			} else {
 
 
@@ -2996,6 +2997,7 @@ THREE.GLTFLoader = ( function () {
 
 
 			if ( nodeDef.name !== undefined ) {
 			if ( nodeDef.name !== undefined ) {
 
 
+				node.userData.name = nodeDef.name;
 				node.name = THREE.PropertyBinding.sanitizeNodeName( nodeDef.name );
 				node.name = THREE.PropertyBinding.sanitizeNodeName( nodeDef.name );
 
 
 			}
 			}

+ 34 - 7
examples/js/postprocessing/EffectComposer.js

@@ -15,10 +15,20 @@ THREE.EffectComposer = function ( renderer, renderTarget ) {
 			stencilBuffer: false
 			stencilBuffer: false
 		};
 		};
 
 
-		var size = renderer.getDrawingBufferSize( new THREE.Vector2() );
-		renderTarget = new THREE.WebGLRenderTarget( size.width, size.height, parameters );
+		var size = renderer.getSize( new THREE.Vector2() );
+		this._pixelRatio = renderer.getPixelRatio();
+		this._width = size.width;
+		this._height = size.height;
+
+		renderTarget = new THREE.WebGLRenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, parameters );
 		renderTarget.texture.name = 'EffectComposer.rt1';
 		renderTarget.texture.name = 'EffectComposer.rt1';
 
 
+	} else {
+
+		this._pixelRatio = 1;
+		this._width = renderTarget.width;
+		this._height = renderTarget.height;
+
 	}
 	}
 
 
 	this.renderTarget1 = renderTarget;
 	this.renderTarget1 = renderTarget;
@@ -162,10 +172,13 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 
 		if ( renderTarget === undefined ) {
 		if ( renderTarget === undefined ) {
 
 
-			var size = this.renderer.getDrawingBufferSize( new THREE.Vector2() );
+			var size = this.renderer.getSize( new THREE.Vector2() );
+			this._pixelRatio = this.renderer.getPixelRatio();
+			this._width = size.width;
+			this._height = size.height;
 
 
 			renderTarget = this.renderTarget1.clone();
 			renderTarget = this.renderTarget1.clone();
-			renderTarget.setSize( size.width, size.height );
+			renderTarget.setSize( this._width * this._pixelRatio, this._height * this._pixelRatio );
 
 
 		}
 		}
 
 
@@ -181,15 +194,29 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 
 	setSize: function ( width, height ) {
 	setSize: function ( width, height ) {
 
 
-		this.renderTarget1.setSize( width, height );
-		this.renderTarget2.setSize( width, height );
+		this._width = width;
+		this._height = height;
+
+		var effectiveWidth = this._width * this._pixelRatio;
+		var effectiveHeight = this._height * this._pixelRatio;
+
+		this.renderTarget1.setSize( effectiveWidth, effectiveHeight );
+		this.renderTarget2.setSize( effectiveWidth, effectiveHeight );
 
 
 		for ( var i = 0; i < this.passes.length; i ++ ) {
 		for ( var i = 0; i < this.passes.length; i ++ ) {
 
 
-			this.passes[ i ].setSize( width, height );
+			this.passes[ i ].setSize( effectiveWidth, effectiveHeight );
 
 
 		}
 		}
 
 
+	},
+
+	setPixelRatio: function ( pixelRatio ) {
+
+		this._pixelRatio = pixelRatio;
+
+		this.setSize( this._width, this._height );
+
 	}
 	}
 
 
 } );
 } );

+ 24 - 0
examples/jsm/controls/DeviceOrientationControls.d.ts

@@ -0,0 +1,24 @@
+import {
+  Camera,
+  Vector3
+} from '../../../src/Three';
+
+export class DeviceOrientationControls {
+  constructor(object: Camera);
+
+  object: Camera;
+
+  // API
+
+  alphaOffset: number;
+  deviceOrientation: any;
+  enabled: boolean;
+  screenOrientation: number;
+  target: Vector3;
+
+  connect(): void;
+  disconnect(): void;
+  dispose(): void;
+  update(): void;
+
+}

+ 120 - 0
examples/jsm/controls/DeviceOrientationControls.js

@@ -0,0 +1,120 @@
+/**
+ * @author richt / http://richt.me
+ * @author WestLangley / http://github.com/WestLangley
+ *
+ * W3C Device Orientation control (http://w3c.github.io/deviceorientation/spec-source-orientation.html)
+ */
+
+import {
+	Euler,
+	Math as _Math,
+	Quaternion,
+	Vector3
+} from "../../../build/three.module.js";
+
+var DeviceOrientationControls = function ( object ) {
+
+	var scope = this;
+
+	this.object = object;
+	this.object.rotation.reorder( 'YXZ' );
+
+	this.enabled = true;
+
+	this.deviceOrientation = {};
+	this.screenOrientation = 0;
+
+	this.alphaOffset = 0; // radians
+
+	var onDeviceOrientationChangeEvent = function ( event ) {
+
+		scope.deviceOrientation = event;
+
+	};
+
+	var onScreenOrientationChangeEvent = function () {
+
+		scope.screenOrientation = window.orientation || 0;
+
+	};
+
+	// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
+
+	var setObjectQuaternion = function () {
+
+		var zee = new Vector3( 0, 0, 1 );
+
+		var euler = new Euler();
+
+		var q0 = new Quaternion();
+
+		var q1 = new Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
+
+		return function ( quaternion, alpha, beta, gamma, orient ) {
+
+			euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
+
+			quaternion.setFromEuler( euler ); // orient the device
+
+			quaternion.multiply( q1 ); // camera looks out the back of the device, not the top
+
+			quaternion.multiply( q0.setFromAxisAngle( zee, - orient ) ); // adjust for screen orientation
+
+		};
+
+	}();
+
+	this.connect = function () {
+
+		onScreenOrientationChangeEvent(); // run once on load
+
+		window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
+		window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
+
+		scope.enabled = true;
+
+	};
+
+	this.disconnect = function () {
+
+		window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent, false );
+		window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent, false );
+
+		scope.enabled = false;
+
+	};
+
+	this.update = function () {
+
+		if ( scope.enabled === false ) return;
+
+		var device = scope.deviceOrientation;
+
+		if ( device ) {
+
+			var alpha = device.alpha ? _Math.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
+
+			var beta = device.beta ? _Math.degToRad( device.beta ) : 0; // X'
+
+			var gamma = device.gamma ? _Math.degToRad( device.gamma ) : 0; // Y''
+
+			var orient = scope.screenOrientation ? _Math.degToRad( scope.screenOrientation ) : 0; // O
+
+			setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
+
+		}
+
+
+	};
+
+	this.dispose = function () {
+
+		scope.disconnect();
+
+	};
+
+	this.connect();
+
+};
+
+export { DeviceOrientationControls };

+ 20 - 0
examples/jsm/controls/DragControls.d.ts

@@ -0,0 +1,20 @@
+import {
+  Camera,
+  EventDispatcher,
+  Object3D
+} from '../../../src/Three';
+
+export class DragControls extends EventDispatcher {
+  constructor(objects: Object3D[], camera: Camera, domElement?: HTMLElement);
+
+  object: Camera;
+
+  // API
+
+  enabled: boolean;
+
+  activate(): void;
+  deactivate(): void;
+  dispose(): void;
+
+}

+ 298 - 0
examples/jsm/controls/DragControls.js

@@ -0,0 +1,298 @@
+/*
+ * @author zz85 / https://github.com/zz85
+ * @author mrdoob / http://mrdoob.com
+ * Running this will allow you to drag three.js objects around the screen.
+ */
+
+import {
+	Camera,
+	EventDispatcher,
+	Matrix4,
+	Plane,
+	Raycaster,
+	Vector2,
+	Vector3
+} from "../../../build/three.module.js";
+
+var DragControls = function ( _objects, _camera, _domElement ) {
+
+	if ( _objects instanceof Camera ) {
+
+		console.warn( 'THREE.DragControls: Constructor now expects ( objects, camera, domElement )' );
+		var temp = _objects; _objects = _camera; _camera = temp;
+
+	}
+
+	var _plane = new Plane();
+	var _raycaster = new Raycaster();
+
+	var _mouse = new Vector2();
+	var _offset = new Vector3();
+	var _intersection = new Vector3();
+	var _worldPosition = new Vector3();
+	var _inverseMatrix = new Matrix4();
+
+	var _selected = null, _hovered = null;
+
+	//
+
+	var scope = this;
+
+	function activate() {
+
+		_domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
+		_domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
+		_domElement.addEventListener( 'mouseup', onDocumentMouseCancel, false );
+		_domElement.addEventListener( 'mouseleave', onDocumentMouseCancel, false );
+		_domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
+		_domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
+		_domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
+
+	}
+
+	function deactivate() {
+
+		_domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
+		_domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
+		_domElement.removeEventListener( 'mouseup', onDocumentMouseCancel, false );
+		_domElement.removeEventListener( 'mouseleave', onDocumentMouseCancel, false );
+		_domElement.removeEventListener( 'touchmove', onDocumentTouchMove, false );
+		_domElement.removeEventListener( 'touchstart', onDocumentTouchStart, false );
+		_domElement.removeEventListener( 'touchend', onDocumentTouchEnd, false );
+
+	}
+
+	function dispose() {
+
+		deactivate();
+
+	}
+
+	function onDocumentMouseMove( event ) {
+
+		event.preventDefault();
+
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		if ( _selected && scope.enabled ) {
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+
+			}
+
+			scope.dispatchEvent( { type: 'drag', object: _selected } );
+
+			return;
+
+		}
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		var intersects = _raycaster.intersectObjects( _objects );
+
+		if ( intersects.length > 0 ) {
+
+			var object = intersects[ 0 ].object;
+
+			_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
+
+			if ( _hovered !== object ) {
+
+				scope.dispatchEvent( { type: 'hoveron', object: object } );
+
+				_domElement.style.cursor = 'pointer';
+				_hovered = object;
+
+			}
+
+		} else {
+
+			if ( _hovered !== null ) {
+
+				scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
+
+				_domElement.style.cursor = 'auto';
+				_hovered = null;
+
+			}
+
+		}
+
+	}
+
+	function onDocumentMouseDown( event ) {
+
+		event.preventDefault();
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		var intersects = _raycaster.intersectObjects( _objects );
+
+		if ( intersects.length > 0 ) {
+
+			_selected = intersects[ 0 ].object;
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_inverseMatrix.getInverse( _selected.parent.matrixWorld );
+				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+
+			}
+
+			_domElement.style.cursor = 'move';
+
+			scope.dispatchEvent( { type: 'dragstart', object: _selected } );
+
+		}
+
+
+	}
+
+	function onDocumentMouseCancel( event ) {
+
+		event.preventDefault();
+
+		if ( _selected ) {
+
+			scope.dispatchEvent( { type: 'dragend', object: _selected } );
+
+			_selected = null;
+
+		}
+
+		_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
+
+	}
+
+	function onDocumentTouchMove( event ) {
+
+		event.preventDefault();
+		event = event.changedTouches[ 0 ];
+
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		if ( _selected && scope.enabled ) {
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+
+			}
+
+			scope.dispatchEvent( { type: 'drag', object: _selected } );
+
+			return;
+
+		}
+
+	}
+
+	function onDocumentTouchStart( event ) {
+
+		event.preventDefault();
+		event = event.changedTouches[ 0 ];
+
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		var intersects = _raycaster.intersectObjects( _objects );
+
+		if ( intersects.length > 0 ) {
+
+			_selected = intersects[ 0 ].object;
+
+			_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_inverseMatrix.getInverse( _selected.parent.matrixWorld );
+				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+
+			}
+
+			_domElement.style.cursor = 'move';
+
+			scope.dispatchEvent( { type: 'dragstart', object: _selected } );
+
+		}
+
+
+	}
+
+	function onDocumentTouchEnd( event ) {
+
+		event.preventDefault();
+
+		if ( _selected ) {
+
+			scope.dispatchEvent( { type: 'dragend', object: _selected } );
+
+			_selected = null;
+
+		}
+
+		_domElement.style.cursor = 'auto';
+
+	}
+
+	activate();
+
+	// API
+
+	this.enabled = true;
+
+	this.activate = activate;
+	this.deactivate = deactivate;
+	this.dispose = dispose;
+
+	// Backward compatibility
+
+	this.setObjects = function () {
+
+		console.error( 'THREE.DragControls: setObjects() has been removed.' );
+
+	};
+
+	this.on = function ( type, listener ) {
+
+		console.warn( 'THREE.DragControls: on() has been deprecated. Use addEventListener() instead.' );
+		scope.addEventListener( type, listener );
+
+	};
+
+	this.off = function ( type, listener ) {
+
+		console.warn( 'THREE.DragControls: off() has been deprecated. Use removeEventListener() instead.' );
+		scope.removeEventListener( type, listener );
+
+	};
+
+	this.notify = function ( type ) {
+
+		console.error( 'THREE.DragControls: notify() has been deprecated. Use dispatchEvent() instead.' );
+		scope.dispatchEvent( { type: type } );
+
+	};
+
+};
+
+DragControls.prototype = Object.create( EventDispatcher.prototype );
+DragControls.prototype.constructor = DragControls;
+
+export { DragControls };

+ 26 - 0
examples/jsm/controls/EditorControls.d.ts

@@ -0,0 +1,26 @@
+import {
+  Camera,
+  EventDispatcher,
+  Vector3,
+  Object3D
+} from '../../../src/Three';
+
+export class EditorControls extends EventDispatcher {
+  constructor(object: Camera, domElement?: HTMLElement);
+
+  object: Camera;
+  domElement: HTMLElement | HTMLDocument;
+
+  enabled: boolean;
+  center: Vector3;
+  panSpeed: number;
+  zoomSpeed: number;
+  rotationSpeed: number;
+
+  focus(target: Object3D): void;
+  pan(delta: Vector3): void;
+  zoom(delta: Vector3): void;
+  rotate(delta: Vector3): void;
+  dispose(): void;
+
+}

+ 327 - 0
examples/jsm/controls/EditorControls.js

@@ -0,0 +1,327 @@
+/**
+ * @author qiao / https://github.com/qiao
+ * @author mrdoob / http://mrdoob.com
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+import {
+	Box3,
+	EventDispatcher,
+	Matrix3,
+	Sphere,
+	Spherical,
+	Vector2,
+	Vector3
+} from "../../../build/three.module.js";
+
+var EditorControls = function ( object, domElement ) {
+
+	domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+	this.center = new Vector3();
+	this.panSpeed = 0.002;
+	this.zoomSpeed = 0.1;
+	this.rotationSpeed = 0.005;
+
+	// internals
+
+	var scope = this;
+	var vector = new Vector3();
+	var delta = new Vector3();
+	var box = new Box3();
+
+	var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2 };
+	var state = STATE.NONE;
+
+	var center = this.center;
+	var normalMatrix = new Matrix3();
+	var pointer = new Vector2();
+	var pointerOld = new Vector2();
+	var spherical = new Spherical();
+	var sphere = new Sphere();
+
+	// events
+
+	var changeEvent = { type: 'change' };
+
+	this.focus = function ( target ) {
+
+		var distance;
+
+		box.setFromObject( target );
+
+		if ( box.isEmpty() === false ) {
+
+			box.getCenter( center );
+			distance = box.getBoundingSphere( sphere ).radius;
+
+		} else {
+
+			// Focusing on an Group, AmbientLight, etc
+
+			center.setFromMatrixPosition( target.matrixWorld );
+			distance = 0.1;
+
+		}
+
+		delta.set( 0, 0, 1 );
+		delta.applyQuaternion( object.quaternion );
+		delta.multiplyScalar( distance * 4 );
+
+		object.position.copy( center ).add( delta );
+
+		scope.dispatchEvent( changeEvent );
+
+	};
+
+	this.pan = function ( delta ) {
+
+		var distance = object.position.distanceTo( center );
+
+		delta.multiplyScalar( distance * scope.panSpeed );
+		delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) );
+
+		object.position.add( delta );
+		center.add( delta );
+
+		scope.dispatchEvent( changeEvent );
+
+	};
+
+	this.zoom = function ( delta ) {
+
+		var distance = object.position.distanceTo( center );
+
+		delta.multiplyScalar( distance * scope.zoomSpeed );
+
+		if ( delta.length() > distance ) return;
+
+		delta.applyMatrix3( normalMatrix.getNormalMatrix( object.matrix ) );
+
+		object.position.add( delta );
+
+		scope.dispatchEvent( changeEvent );
+
+	};
+
+	this.rotate = function ( delta ) {
+
+		vector.copy( object.position ).sub( center );
+
+		spherical.setFromVector3( vector );
+
+		spherical.theta += delta.x * scope.rotationSpeed;
+		spherical.phi += delta.y * scope.rotationSpeed;
+
+		spherical.makeSafe();
+
+		vector.setFromSpherical( spherical );
+
+		object.position.copy( center ).add( vector );
+
+		object.lookAt( center );
+
+		scope.dispatchEvent( changeEvent );
+
+	};
+
+	// mouse
+
+	function onMouseDown( event ) {
+
+		if ( scope.enabled === false ) return;
+
+		if ( event.button === 0 ) {
+
+			state = STATE.ROTATE;
+
+		} else if ( event.button === 1 ) {
+
+			state = STATE.ZOOM;
+
+		} else if ( event.button === 2 ) {
+
+			state = STATE.PAN;
+
+		}
+
+		pointerOld.set( event.clientX, event.clientY );
+
+		domElement.addEventListener( 'mousemove', onMouseMove, false );
+		domElement.addEventListener( 'mouseup', onMouseUp, false );
+		domElement.addEventListener( 'mouseout', onMouseUp, false );
+		domElement.addEventListener( 'dblclick', onMouseUp, false );
+
+	}
+
+	function onMouseMove( event ) {
+
+		if ( scope.enabled === false ) return;
+
+		pointer.set( event.clientX, event.clientY );
+
+		var movementX = pointer.x - pointerOld.x;
+		var movementY = pointer.y - pointerOld.y;
+
+		if ( state === STATE.ROTATE ) {
+
+			scope.rotate( delta.set( - movementX, - movementY, 0 ) );
+
+		} else if ( state === STATE.ZOOM ) {
+
+			scope.zoom( delta.set( 0, 0, movementY ) );
+
+		} else if ( state === STATE.PAN ) {
+
+			scope.pan( delta.set( - movementX, movementY, 0 ) );
+
+		}
+
+		pointerOld.set( event.clientX, event.clientY );
+
+	}
+
+	function onMouseUp( event ) {
+
+		domElement.removeEventListener( 'mousemove', onMouseMove, false );
+		domElement.removeEventListener( 'mouseup', onMouseUp, false );
+		domElement.removeEventListener( 'mouseout', onMouseUp, false );
+		domElement.removeEventListener( 'dblclick', onMouseUp, false );
+
+		state = STATE.NONE;
+
+	}
+
+	function onMouseWheel( event ) {
+
+		event.preventDefault();
+
+		// Normalize deltaY due to https://bugzilla.mozilla.org/show_bug.cgi?id=1392460
+		scope.zoom( delta.set( 0, 0, event.deltaY > 0 ? 1 : - 1 ) );
+
+	}
+
+	function contextmenu( event ) {
+
+		event.preventDefault();
+
+	}
+
+	this.dispose = function () {
+
+		domElement.removeEventListener( 'contextmenu', contextmenu, false );
+		domElement.removeEventListener( 'mousedown', onMouseDown, false );
+		domElement.removeEventListener( 'wheel', onMouseWheel, false );
+
+		domElement.removeEventListener( 'mousemove', onMouseMove, false );
+		domElement.removeEventListener( 'mouseup', onMouseUp, false );
+		domElement.removeEventListener( 'mouseout', onMouseUp, false );
+		domElement.removeEventListener( 'dblclick', onMouseUp, false );
+
+		domElement.removeEventListener( 'touchstart', touchStart, false );
+		domElement.removeEventListener( 'touchmove', touchMove, false );
+
+	};
+
+	domElement.addEventListener( 'contextmenu', contextmenu, false );
+	domElement.addEventListener( 'mousedown', onMouseDown, false );
+	domElement.addEventListener( 'wheel', onMouseWheel, false );
+
+	// touch
+
+	var touches = [ new Vector3(), new Vector3(), new Vector3() ];
+	var prevTouches = [ new Vector3(), new Vector3(), new Vector3() ];
+
+	var prevDistance = null;
+
+	function touchStart( event ) {
+
+		if ( scope.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				touches[ 1 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				break;
+
+			case 2:
+				touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				touches[ 1 ].set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				prevDistance = touches[ 0 ].distanceTo( touches[ 1 ] );
+				break;
+
+		}
+
+		prevTouches[ 0 ].copy( touches[ 0 ] );
+		prevTouches[ 1 ].copy( touches[ 1 ] );
+
+	}
+
+
+	function touchMove( event ) {
+
+		if ( scope.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		function getClosest( touch, touches ) {
+
+			var closest = touches[ 0 ];
+
+			for ( var i in touches ) {
+
+				if ( closest.distanceTo( touch ) > touches[ i ].distanceTo( touch ) ) closest = touches[ i ];
+
+			}
+
+			return closest;
+
+		}
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				touches[ 1 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				scope.rotate( touches[ 0 ].sub( getClosest( touches[ 0 ], prevTouches ) ).multiplyScalar( - 1 ) );
+				break;
+
+			case 2:
+				touches[ 0 ].set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				touches[ 1 ].set( event.touches[ 1 ].pageX, event.touches[ 1 ].pageY, 0 ).divideScalar( window.devicePixelRatio );
+				var distance = touches[ 0 ].distanceTo( touches[ 1 ] );
+				scope.zoom( delta.set( 0, 0, prevDistance - distance ) );
+				prevDistance = distance;
+
+
+				var offset0 = touches[ 0 ].clone().sub( getClosest( touches[ 0 ], prevTouches ) );
+				var offset1 = touches[ 1 ].clone().sub( getClosest( touches[ 1 ], prevTouches ) );
+				offset0.x = - offset0.x;
+				offset1.x = - offset1.x;
+
+				scope.pan( offset0.add( offset1 ) );
+
+				break;
+
+		}
+
+		prevTouches[ 0 ].copy( touches[ 0 ] );
+		prevTouches[ 1 ].copy( touches[ 1 ] );
+
+	}
+
+	domElement.addEventListener( 'touchstart', touchStart, false );
+	domElement.addEventListener( 'touchmove', touchMove, false );
+
+};
+
+EditorControls.prototype = Object.create( EventDispatcher.prototype );
+EditorControls.prototype.constructor = EditorControls;
+
+export { EditorControls };

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

@@ -0,0 +1,25 @@
+import {
+  Camera,
+  EventDispatcher,
+  Vector3
+} from '../../../src/Three';
+
+export class PointerLockControls extends EventDispatcher {
+  constructor(camera: Camera, domElement?: HTMLElement);
+
+  domElement: HTMLElement;
+  object: Camera;
+
+  // API
+
+  isLocked: boolean;
+
+  connect(): void;
+  disconnect(): void;
+  dispose(): void;
+  getObject(): Camera;
+  getDirection(v: Vector3): Vector3;
+  lock(): void;
+  unlock(): void;
+
+}

+ 134 - 0
examples/jsm/controls/PointerLockControls.js

@@ -0,0 +1,134 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author Mugen87 / https://github.com/Mugen87
+ */
+
+import {
+	Euler,
+	EventDispatcher,
+	Vector3
+} from "../../../build/three.module.js";
+
+var PointerLockControls = function ( camera, domElement ) {
+
+	this.domElement = domElement || document.body;
+	this.isLocked = false;
+
+	//
+	// internals
+	//
+
+	var scope = this;
+
+	var changeEvent = { type: 'change' };
+	var lockEvent = { type: 'lock' };
+	var unlockEvent = { type: 'unlock' };
+
+	var euler = new Euler( 0, 0, 0, 'YXZ' );
+
+	var PI_2 = Math.PI / 2;
+
+	function onMouseMove( event ) {
+
+		if ( scope.isLocked === false ) return;
+
+		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
+		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
+
+		euler.setFromQuaternion( camera.quaternion );
+
+		euler.y -= movementX * 0.002;
+		euler.x -= movementY * 0.002;
+
+		euler.x = Math.max( - PI_2, Math.min( PI_2, euler.x ) );
+
+		camera.quaternion.setFromEuler( euler );
+
+		scope.dispatchEvent( changeEvent );
+
+	}
+
+	function onPointerlockChange() {
+
+		if ( document.pointerLockElement === scope.domElement ) {
+
+			scope.dispatchEvent( lockEvent );
+
+			scope.isLocked = true;
+
+		} else {
+
+			scope.dispatchEvent( unlockEvent );
+
+			scope.isLocked = false;
+
+		}
+
+	}
+
+	function onPointerlockError() {
+
+		console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
+
+	}
+
+	this.connect = function () {
+
+		document.addEventListener( 'mousemove', onMouseMove, false );
+		document.addEventListener( 'pointerlockchange', onPointerlockChange, false );
+		document.addEventListener( 'pointerlockerror', onPointerlockError, false );
+
+	};
+
+	this.disconnect = function () {
+
+		document.removeEventListener( 'mousemove', onMouseMove, false );
+		document.removeEventListener( 'pointerlockchange', onPointerlockChange, false );
+		document.removeEventListener( 'pointerlockerror', onPointerlockError, false );
+
+	};
+
+	this.dispose = function () {
+
+		this.disconnect();
+
+	};
+
+	this.getObject = function () { // retaining this method for backward compatibility
+
+		return camera;
+
+	};
+
+	this.getDirection = function () {
+
+		var direction = new Vector3( 0, 0, - 1 );
+
+		return function ( v ) {
+
+			return v.copy( direction ).applyQuaternion( camera.quaternion );
+
+		};
+
+	}();
+
+	this.lock = function () {
+
+		this.domElement.requestPointerLock();
+
+	};
+
+	this.unlock = function () {
+
+		document.exitPointerLock();
+
+	};
+
+	this.connect();
+
+};
+
+PointerLockControls.prototype = Object.create( EventDispatcher.prototype );
+PointerLockControls.prototype.constructor = PointerLockControls;
+
+export { PointerLockControls };

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

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

+ 6 - 1
examples/jsm/exporters/GLTFExporter.js

@@ -1764,7 +1764,12 @@ GLTFExporter.prototype = {
 
 
 			} else {
 			} else {
 
 
-				object.updateMatrix();
+				if ( object.matrixAutoUpdate ) {
+
+					object.updateMatrix();
+
+				}
+
 				if ( ! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
 				if ( ! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
 
 
 					gltfNode.matrix = object.matrix.elements;
 					gltfNode.matrix = object.matrix.elements;

+ 24 - 0
examples/jsm/loaders/BVHLoader.d.ts

@@ -0,0 +1,24 @@
+import {
+  AnimationClip,
+  Skeleton,
+  LoadingManager
+} from '../../../src/Three';
+
+
+export interface BVH {
+  clip: AnimationClip,
+  skeleton: Skeleton;
+}
+
+export class BVHLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+  animateBonePositions: boolean;
+  animateBoneRotations: boolean;
+
+  load(url: string, onLoad: (bvh: BVH) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+  setPath(path: string) : this;
+
+  parse(text: string) : BVH;
+}

+ 428 - 0
examples/jsm/loaders/BVHLoader.js

@@ -0,0 +1,428 @@
+/**
+ * @author herzig / http://github.com/herzig
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * Description: reads BVH files and outputs a single Skeleton and an AnimationClip
+ *
+ * Currently only supports bvh files containing a single root.
+ *
+ */
+
+import {
+	AnimationClip,
+	Bone,
+	DefaultLoadingManager,
+	FileLoader,
+	Quaternion,
+	QuaternionKeyframeTrack,
+	Skeleton,
+	Vector3,
+	VectorKeyframeTrack
+} from "../../../build/three.module.js";
+
+var BVHLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+	this.animateBonePositions = true;
+	this.animateBoneRotations = true;
+
+};
+
+BVHLoader.prototype = {
+
+	constructor: BVHLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( text ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( text ) {
+
+		/*
+			reads a string array (lines) from a BVH file
+			and outputs a skeleton structure including motion data
+
+			returns thee root node:
+			{ name: '', channels: [], children: [] }
+		*/
+		function readBvh( lines ) {
+
+			// read model structure
+
+			if ( nextLine( lines ) !== 'HIERARCHY' ) {
+
+				console.error( 'THREE.BVHLoader: HIERARCHY expected.' );
+
+			}
+
+			var list = []; // collects flat array of all bones
+			var root = readNode( lines, nextLine( lines ), list );
+
+			// read motion data
+
+			if ( nextLine( lines ) !== 'MOTION' ) {
+
+				console.error( 'THREE.BVHLoader: MOTION expected.' );
+
+			}
+
+			// number of frames
+
+			var tokens = nextLine( lines ).split( /[\s]+/ );
+			var numFrames = parseInt( tokens[ 1 ] );
+
+			if ( isNaN( numFrames ) ) {
+
+				console.error( 'THREE.BVHLoader: Failed to read number of frames.' );
+
+			}
+
+			// frame time
+
+			tokens = nextLine( lines ).split( /[\s]+/ );
+			var frameTime = parseFloat( tokens[ 2 ] );
+
+			if ( isNaN( frameTime ) ) {
+
+				console.error( 'THREE.BVHLoader: Failed to read frame time.' );
+
+			}
+
+			// read frame data line by line
+
+			for ( var i = 0; i < numFrames; i ++ ) {
+
+				tokens = nextLine( lines ).split( /[\s]+/ );
+				readFrameData( tokens, i * frameTime, root );
+
+			}
+
+			return list;
+
+		}
+
+		/*
+			Recursively reads data from a single frame into the bone hierarchy.
+			The passed bone hierarchy has to be structured in the same order as the BVH file.
+			keyframe data is stored in bone.frames.
+
+			- data: splitted string array (frame values), values are shift()ed so
+			this should be empty after parsing the whole hierarchy.
+			- frameTime: playback time for this keyframe.
+			- bone: the bone to read frame data from.
+		*/
+		function readFrameData( data, frameTime, bone ) {
+
+			// end sites have no motion data
+
+			if ( bone.type === 'ENDSITE' ) return;
+
+			// add keyframe
+
+			var keyframe = {
+				time: frameTime,
+				position: new Vector3(),
+				rotation: new Quaternion()
+			};
+
+			bone.frames.push( keyframe );
+
+			var quat = new Quaternion();
+
+			var vx = new Vector3( 1, 0, 0 );
+			var vy = new Vector3( 0, 1, 0 );
+			var vz = new Vector3( 0, 0, 1 );
+
+			// parse values for each channel in node
+
+			for ( var i = 0; i < bone.channels.length; i ++ ) {
+
+				switch ( bone.channels[ i ] ) {
+
+					case 'Xposition':
+						keyframe.position.x = parseFloat( data.shift().trim() );
+						break;
+					case 'Yposition':
+						keyframe.position.y = parseFloat( data.shift().trim() );
+						break;
+					case 'Zposition':
+						keyframe.position.z = parseFloat( data.shift().trim() );
+						break;
+					case 'Xrotation':
+						quat.setFromAxisAngle( vx, parseFloat( data.shift().trim() ) * Math.PI / 180 );
+						keyframe.rotation.multiply( quat );
+						break;
+					case 'Yrotation':
+						quat.setFromAxisAngle( vy, parseFloat( data.shift().trim() ) * Math.PI / 180 );
+						keyframe.rotation.multiply( quat );
+						break;
+					case 'Zrotation':
+						quat.setFromAxisAngle( vz, parseFloat( data.shift().trim() ) * Math.PI / 180 );
+						keyframe.rotation.multiply( quat );
+						break;
+					default:
+						console.warn( 'THREE.BVHLoader: Invalid channel type.' );
+
+				}
+
+			}
+
+			// parse child nodes
+
+			for ( var i = 0; i < bone.children.length; i ++ ) {
+
+				readFrameData( data, frameTime, bone.children[ i ] );
+
+			}
+
+		}
+
+		/*
+		 Recursively parses the HIERACHY section of the BVH file
+
+		 - lines: all lines of the file. lines are consumed as we go along.
+		 - firstline: line containing the node type and name e.g. 'JOINT hip'
+		 - list: collects a flat list of nodes
+
+		 returns: a BVH node including children
+		*/
+		function readNode( lines, firstline, list ) {
+
+			var node = { name: '', type: '', frames: [] };
+			list.push( node );
+
+			// parse node type and name
+
+			var tokens = firstline.split( /[\s]+/ );
+
+			if ( tokens[ 0 ].toUpperCase() === 'END' && tokens[ 1 ].toUpperCase() === 'SITE' ) {
+
+				node.type = 'ENDSITE';
+				node.name = 'ENDSITE'; // bvh end sites have no name
+
+			} else {
+
+				node.name = tokens[ 1 ];
+				node.type = tokens[ 0 ].toUpperCase();
+
+			}
+
+			if ( nextLine( lines ) !== '{' ) {
+
+				console.error( 'THREE.BVHLoader: Expected opening { after type & name' );
+
+			}
+
+			// parse OFFSET
+
+			tokens = nextLine( lines ).split( /[\s]+/ );
+
+			if ( tokens[ 0 ] !== 'OFFSET' ) {
+
+				console.error( 'THREE.BVHLoader: Expected OFFSET but got: ' + tokens[ 0 ] );
+
+			}
+
+			if ( tokens.length !== 4 ) {
+
+				console.error( 'THREE.BVHLoader: Invalid number of values for OFFSET.' );
+
+			}
+
+			var offset = new Vector3(
+				parseFloat( tokens[ 1 ] ),
+				parseFloat( tokens[ 2 ] ),
+				parseFloat( tokens[ 3 ] )
+			);
+
+			if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) {
+
+				console.error( 'THREE.BVHLoader: Invalid values of OFFSET.' );
+
+			}
+
+			node.offset = offset;
+
+			// parse CHANNELS definitions
+
+			if ( node.type !== 'ENDSITE' ) {
+
+				tokens = nextLine( lines ).split( /[\s]+/ );
+
+				if ( tokens[ 0 ] !== 'CHANNELS' ) {
+
+					console.error( 'THREE.BVHLoader: Expected CHANNELS definition.' );
+
+				}
+
+				var numChannels = parseInt( tokens[ 1 ] );
+				node.channels = tokens.splice( 2, numChannels );
+				node.children = [];
+
+			}
+
+			// read children
+
+			while ( true ) {
+
+				var line = nextLine( lines );
+
+				if ( line === '}' ) {
+
+					return node;
+
+				} else {
+
+					node.children.push( readNode( lines, line, list ) );
+
+				}
+
+			}
+
+		}
+
+		/*
+			recursively converts the internal bvh node structure to a Bone hierarchy
+
+			source: the bvh root node
+			list: pass an empty array, collects a flat list of all converted THREE.Bones
+
+			returns the root Bone
+		*/
+		function toTHREEBone( source, list ) {
+
+			var bone = new Bone();
+			list.push( bone );
+
+			bone.position.add( source.offset );
+			bone.name = source.name;
+
+			if ( source.type !== 'ENDSITE' ) {
+
+				for ( var i = 0; i < source.children.length; i ++ ) {
+
+					bone.add( toTHREEBone( source.children[ i ], list ) );
+
+				}
+
+			}
+
+			return bone;
+
+		}
+
+		/*
+			builds a AnimationClip from the keyframe data saved in each bone.
+
+			bone: bvh root node
+
+			returns: a AnimationClip containing position and quaternion tracks
+		*/
+		function toTHREEAnimation( bones ) {
+
+			var tracks = [];
+
+			// create a position and quaternion animation track for each node
+
+			for ( var i = 0; i < bones.length; i ++ ) {
+
+				var bone = bones[ i ];
+
+				if ( bone.type === 'ENDSITE' )
+					continue;
+
+				// track data
+
+				var times = [];
+				var positions = [];
+				var rotations = [];
+
+				for ( var j = 0; j < bone.frames.length; j ++ ) {
+
+					var frame = bone.frames[ j ];
+
+					times.push( frame.time );
+
+					// the animation system animates the position property,
+					// so we have to add the joint offset to all values
+
+					positions.push( frame.position.x + bone.offset.x );
+					positions.push( frame.position.y + bone.offset.y );
+					positions.push( frame.position.z + bone.offset.z );
+
+					rotations.push( frame.rotation.x );
+					rotations.push( frame.rotation.y );
+					rotations.push( frame.rotation.z );
+					rotations.push( frame.rotation.w );
+
+				}
+
+				if ( scope.animateBonePositions ) {
+
+					tracks.push( new VectorKeyframeTrack( '.bones[' + bone.name + '].position', times, positions ) );
+
+				}
+
+				if ( scope.animateBoneRotations ) {
+
+					tracks.push( new QuaternionKeyframeTrack( '.bones[' + bone.name + '].quaternion', times, rotations ) );
+
+				}
+
+			}
+
+			return new AnimationClip( 'animation', - 1, tracks );
+
+		}
+
+		/*
+			returns the next non-empty line in lines
+		*/
+		function nextLine( lines ) {
+
+			var line;
+			// skip empty lines
+			while ( ( line = lines.shift().trim() ).length === 0 ) { }
+			return line;
+
+		}
+
+		var scope = this;
+
+		var lines = text.split( /[\r\n]+/g );
+
+		var bones = readBvh( lines );
+
+		var threeBones = [];
+		toTHREEBone( bones[ 0 ], threeBones );
+
+		var threeClip = toTHREEAnimation( bones );
+
+		return {
+			skeleton: new Skeleton( threeBones ),
+			clip: threeClip
+		};
+
+	}
+
+};
+
+export { BVHLoader };

+ 4 - 2
examples/jsm/loaders/GLTFLoader.js

@@ -933,7 +933,8 @@ var GLTFLoader = ( function () {
 
 
 				for ( var i = 0, il = params.length; i < il; i ++ ) {
 				for ( var i = 0, il = params.length; i < il; i ++ ) {
 
 
-					target[ params[ i ] ] = source[ params[ i ] ];
+					var value = source[ params[ i ] ];
+					target[ params[ i ] ] = value.isColor ? value.clone() : value;
 
 
 				}
 				}
 
 
@@ -1391,7 +1392,7 @@ var GLTFLoader = ( function () {
 
 
 			if ( typeof gltfDef.extras === 'object' ) {
 			if ( typeof gltfDef.extras === 'object' ) {
 
 
-				object.userData = gltfDef.extras;
+				Object.assign( object.userData, gltfDef.extras );
 
 
 			} else {
 			} else {
 
 
@@ -3088,6 +3089,7 @@ var GLTFLoader = ( function () {
 
 
 			if ( nodeDef.name !== undefined ) {
 			if ( nodeDef.name !== undefined ) {
 
 
+				node.userData.name = nodeDef.name;
 				node.name = PropertyBinding.sanitizeNodeName( nodeDef.name );
 				node.name = PropertyBinding.sanitizeNodeName( nodeDef.name );
 
 
 			}
 			}

+ 17 - 0
examples/jsm/loaders/PCDLoader.d.ts

@@ -0,0 +1,17 @@
+import {
+  Points,
+  LoadingManager
+} from '../../../src/Three';
+
+
+export class PCDLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  littleEndian: boolean;
+  path: string;
+
+  load(url: string, onLoad: (points: Points) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+  setPath(path: string) : this;
+
+  parse(data: ArrayBuffer | string, url: string) : Points;
+}

+ 321 - 0
examples/jsm/loaders/PCDLoader.js

@@ -0,0 +1,321 @@
+/**
+ * @author Filipe Caixeta / http://filipecaixeta.com.br
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * Description: A THREE loader for PCD ascii and binary files.
+ *
+ * Limitations: Compressed binary files are not supported.
+ *
+ */
+
+import {
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	LoaderUtils,
+	Points,
+	PointsMaterial,
+	VertexColors
+} from "../../../build/three.module.js";
+
+var PCDLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+	this.littleEndian = true;
+
+};
+
+
+PCDLoader.prototype = {
+
+	constructor: PCDLoader,
+
+	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 ( data ) {
+
+			try {
+
+				onLoad( scope.parse( data, url ) );
+
+			} catch ( e ) {
+
+				if ( onError ) {
+
+					onError( e );
+
+				} else {
+
+					throw e;
+
+				}
+
+			}
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( data, url ) {
+
+		function parseHeader( data ) {
+
+			var PCDheader = {};
+			var result1 = data.search( /[\r\n]DATA\s(\S*)\s/i );
+			var result2 = /[\r\n]DATA\s(\S*)\s/i.exec( data.substr( result1 - 1 ) );
+
+			PCDheader.data = result2[ 1 ];
+			PCDheader.headerLen = result2[ 0 ].length + result1;
+			PCDheader.str = data.substr( 0, PCDheader.headerLen );
+
+			// remove comments
+
+			PCDheader.str = PCDheader.str.replace( /\#.*/gi, '' );
+
+			// parse
+
+			PCDheader.version = /VERSION (.*)/i.exec( PCDheader.str );
+			PCDheader.fields = /FIELDS (.*)/i.exec( PCDheader.str );
+			PCDheader.size = /SIZE (.*)/i.exec( PCDheader.str );
+			PCDheader.type = /TYPE (.*)/i.exec( PCDheader.str );
+			PCDheader.count = /COUNT (.*)/i.exec( PCDheader.str );
+			PCDheader.width = /WIDTH (.*)/i.exec( PCDheader.str );
+			PCDheader.height = /HEIGHT (.*)/i.exec( PCDheader.str );
+			PCDheader.viewpoint = /VIEWPOINT (.*)/i.exec( PCDheader.str );
+			PCDheader.points = /POINTS (.*)/i.exec( PCDheader.str );
+
+			// evaluate
+
+			if ( PCDheader.version !== null )
+				PCDheader.version = parseFloat( PCDheader.version[ 1 ] );
+
+			if ( PCDheader.fields !== null )
+				PCDheader.fields = PCDheader.fields[ 1 ].split( ' ' );
+
+			if ( PCDheader.type !== null )
+				PCDheader.type = PCDheader.type[ 1 ].split( ' ' );
+
+			if ( PCDheader.width !== null )
+				PCDheader.width = parseInt( PCDheader.width[ 1 ] );
+
+			if ( PCDheader.height !== null )
+				PCDheader.height = parseInt( PCDheader.height[ 1 ] );
+
+			if ( PCDheader.viewpoint !== null )
+				PCDheader.viewpoint = PCDheader.viewpoint[ 1 ];
+
+			if ( PCDheader.points !== null )
+				PCDheader.points = parseInt( PCDheader.points[ 1 ], 10 );
+
+			if ( PCDheader.points === null )
+				PCDheader.points = PCDheader.width * PCDheader.height;
+
+			if ( PCDheader.size !== null ) {
+
+				PCDheader.size = PCDheader.size[ 1 ].split( ' ' ).map( function ( x ) {
+
+					return parseInt( x, 10 );
+
+				} );
+
+			}
+
+			if ( PCDheader.count !== null ) {
+
+				PCDheader.count = PCDheader.count[ 1 ].split( ' ' ).map( function ( x ) {
+
+					return parseInt( x, 10 );
+
+				} );
+
+			} else {
+
+				PCDheader.count = [];
+
+				for ( var i = 0, l = PCDheader.fields.length; i < l; i ++ ) {
+
+					PCDheader.count.push( 1 );
+
+				}
+
+			}
+
+			PCDheader.offset = {};
+
+			var sizeSum = 0;
+
+			for ( var i = 0, l = PCDheader.fields.length; i < l; i ++ ) {
+
+				if ( PCDheader.data === 'ascii' ) {
+
+					PCDheader.offset[ PCDheader.fields[ i ] ] = i;
+
+				} else {
+
+					PCDheader.offset[ PCDheader.fields[ i ] ] = sizeSum;
+					sizeSum += PCDheader.size[ i ];
+
+				}
+
+			}
+
+			// for binary only
+
+			PCDheader.rowSize = sizeSum;
+
+			return PCDheader;
+
+		}
+
+		var textData = LoaderUtils.decodeText( new Uint8Array( data ) );
+
+		// parse header (always ascii format)
+
+		var PCDheader = parseHeader( textData );
+
+		// parse data
+
+		var position = [];
+		var normal = [];
+		var color = [];
+
+		// ascii
+
+		if ( PCDheader.data === 'ascii' ) {
+
+			var offset = PCDheader.offset;
+			var pcdData = textData.substr( PCDheader.headerLen );
+			var lines = pcdData.split( '\n' );
+
+			for ( var i = 0, l = lines.length; i < l; i ++ ) {
+
+				if ( lines[ i ] === '' ) continue;
+
+				var line = lines[ i ].split( ' ' );
+
+				if ( offset.x !== undefined ) {
+
+					position.push( parseFloat( line[ offset.x ] ) );
+					position.push( parseFloat( line[ offset.y ] ) );
+					position.push( parseFloat( line[ offset.z ] ) );
+
+				}
+
+				if ( offset.rgb !== undefined ) {
+
+					var rgb = parseFloat( line[ offset.rgb ] );
+					var r = ( rgb >> 16 ) & 0x0000ff;
+					var g = ( rgb >> 8 ) & 0x0000ff;
+					var b = ( rgb >> 0 ) & 0x0000ff;
+					color.push( r / 255, g / 255, b / 255 );
+
+				}
+
+				if ( offset.normal_x !== undefined ) {
+
+					normal.push( parseFloat( line[ offset.normal_x ] ) );
+					normal.push( parseFloat( line[ offset.normal_y ] ) );
+					normal.push( parseFloat( line[ offset.normal_z ] ) );
+
+				}
+
+			}
+
+		}
+
+		// binary
+
+		if ( PCDheader.data === 'binary_compressed' ) {
+
+			console.error( 'THREE.PCDLoader: binary_compressed files are not supported' );
+			return;
+
+		}
+
+		if ( PCDheader.data === 'binary' ) {
+
+			var dataview = new DataView( data, PCDheader.headerLen );
+			var offset = PCDheader.offset;
+
+			for ( var i = 0, row = 0; i < PCDheader.points; i ++, row += PCDheader.rowSize ) {
+
+				if ( offset.x !== undefined ) {
+
+					position.push( dataview.getFloat32( row + offset.x, this.littleEndian ) );
+					position.push( dataview.getFloat32( row + offset.y, this.littleEndian ) );
+					position.push( dataview.getFloat32( row + offset.z, this.littleEndian ) );
+
+				}
+
+				if ( offset.rgb !== undefined ) {
+
+					color.push( dataview.getUint8( row + offset.rgb + 2 ) / 255.0 );
+					color.push( dataview.getUint8( row + offset.rgb + 1 ) / 255.0 );
+					color.push( dataview.getUint8( row + offset.rgb + 0 ) / 255.0 );
+
+				}
+
+				if ( offset.normal_x !== undefined ) {
+
+					normal.push( dataview.getFloat32( row + offset.normal_x, this.littleEndian ) );
+					normal.push( dataview.getFloat32( row + offset.normal_y, this.littleEndian ) );
+					normal.push( dataview.getFloat32( row + offset.normal_z, this.littleEndian ) );
+
+				}
+
+			}
+
+		}
+
+		// build geometry
+
+		var geometry = new BufferGeometry();
+
+		if ( position.length > 0 ) geometry.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
+		if ( normal.length > 0 ) geometry.addAttribute( 'normal', new Float32BufferAttribute( normal, 3 ) );
+		if ( color.length > 0 ) geometry.addAttribute( 'color', new Float32BufferAttribute( color, 3 ) );
+
+		geometry.computeBoundingSphere();
+
+		// build material
+
+		var material = new PointsMaterial( { size: 0.005 } );
+
+		if ( color.length > 0 ) {
+
+			material.vertexColors = VertexColors;
+
+		} else {
+
+			material.color.setHex( Math.random() * 0xffffff );
+
+		}
+
+		// build mesh
+
+		var mesh = new Points( geometry, material );
+		var name = url.split( '' ).reverse().join( '' );
+		name = /([^\/]*)/.exec( name );
+		name = name[ 1 ].split( '' ).reverse().join( '' );
+		mesh.name = name;
+
+		return mesh;
+
+	}
+
+};
+
+export { PCDLoader };

+ 18 - 0
examples/jsm/loaders/PLYLoader.d.ts

@@ -0,0 +1,18 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+
+export class PLYLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  propertyNameMapping: object;
+  path: string;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+  setPath(path: string) : this;
+  setPropertyNameMapping(mapping: object) : void;
+
+  parse(data: ArrayBuffer | string) : BufferGeometry;
+}

+ 515 - 0
examples/jsm/loaders/PLYLoader.js

@@ -0,0 +1,515 @@
+/**
+ * @author Wei Meng / http://about.me/menway
+ *
+ * Description: A THREE loader for PLY ASCII files (known as the Polygon
+ * File Format or the Stanford Triangle Format).
+ *
+ * Limitations: ASCII decoding assumes file is UTF-8.
+ *
+ * Usage:
+ *	var loader = new PLYLoader();
+ *	loader.load('./models/ply/ascii/dolphins.ply', function (geometry) {
+ *
+ *		scene.add( new THREE.Mesh( geometry ) );
+ *
+ *	} );
+ *
+ * If the PLY file uses non standard property names, they can be mapped while
+ * loading. For example, the following maps the properties
+ * “diffuse_(red|green|blue)” in the file to standard color names.
+ *
+ * loader.setPropertyNameMapping( {
+ *	diffuse_red: 'red',
+ *	diffuse_green: 'green',
+ *	diffuse_blue: 'blue'
+ * } );
+ *
+ */
+
+import {
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	LoaderUtils
+} from "../../../build/three.module.js";
+
+
+var PLYLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+	this.propertyNameMapping = {};
+
+};
+
+PLYLoader.prototype = {
+
+	constructor: PLYLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( text ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	setPropertyNameMapping: function ( mapping ) {
+
+		this.propertyNameMapping = mapping;
+
+	},
+
+	parse: function ( data ) {
+
+		function parseHeader( data ) {
+
+			var patternHeader = /ply([\s\S]*)end_header\r?\n/;
+			var headerText = '';
+			var headerLength = 0;
+			var result = patternHeader.exec( data );
+
+			if ( result !== null ) {
+
+				headerText = result[ 1 ];
+				headerLength = result[ 0 ].length;
+
+			}
+
+			var header = {
+				comments: [],
+				elements: [],
+				headerLength: headerLength
+			};
+
+			var lines = headerText.split( '\n' );
+			var currentElement;
+			var lineType, lineValues;
+
+			function make_ply_element_property( propertValues, propertyNameMapping ) {
+
+				var property = { type: propertValues[ 0 ] };
+
+				if ( property.type === 'list' ) {
+
+					property.name = propertValues[ 3 ];
+					property.countType = propertValues[ 1 ];
+					property.itemType = propertValues[ 2 ];
+
+				} else {
+
+					property.name = propertValues[ 1 ];
+
+				}
+
+				if ( property.name in propertyNameMapping ) {
+
+					property.name = propertyNameMapping[ property.name ];
+
+				}
+
+				return property;
+
+			}
+
+			for ( var i = 0; i < lines.length; i ++ ) {
+
+				var line = lines[ i ];
+				line = line.trim();
+
+				if ( line === '' ) continue;
+
+				lineValues = line.split( /\s+/ );
+				lineType = lineValues.shift();
+				line = lineValues.join( ' ' );
+
+				switch ( lineType ) {
+
+					case 'format':
+
+						header.format = lineValues[ 0 ];
+						header.version = lineValues[ 1 ];
+
+						break;
+
+					case 'comment':
+
+						header.comments.push( line );
+
+						break;
+
+					case 'element':
+
+						if ( currentElement !== undefined ) {
+
+							header.elements.push( currentElement );
+
+						}
+
+						currentElement = {};
+						currentElement.name = lineValues[ 0 ];
+						currentElement.count = parseInt( lineValues[ 1 ] );
+						currentElement.properties = [];
+
+						break;
+
+					case 'property':
+
+						currentElement.properties.push( make_ply_element_property( lineValues, scope.propertyNameMapping ) );
+
+						break;
+
+
+					default:
+
+						console.log( 'unhandled', lineType, lineValues );
+
+				}
+
+			}
+
+			if ( currentElement !== undefined ) {
+
+				header.elements.push( currentElement );
+
+			}
+
+			return header;
+
+		}
+
+		function parseASCIINumber( n, type ) {
+
+			switch ( type ) {
+
+				case 'char': case 'uchar': case 'short': case 'ushort': case 'int': case 'uint':
+				case 'int8': case 'uint8': case 'int16': case 'uint16': case 'int32': case 'uint32':
+
+					return parseInt( n );
+
+				case 'float': case 'double': case 'float32': case 'float64':
+
+					return parseFloat( n );
+
+			}
+
+		}
+
+		function parseASCIIElement( properties, line ) {
+
+			var values = line.split( /\s+/ );
+
+			var element = {};
+
+			for ( var i = 0; i < properties.length; i ++ ) {
+
+				if ( properties[ i ].type === 'list' ) {
+
+					var list = [];
+					var n = parseASCIINumber( values.shift(), properties[ i ].countType );
+
+					for ( var j = 0; j < n; j ++ ) {
+
+						list.push( parseASCIINumber( values.shift(), properties[ i ].itemType ) );
+
+					}
+
+					element[ properties[ i ].name ] = list;
+
+				} else {
+
+					element[ properties[ i ].name ] = parseASCIINumber( values.shift(), properties[ i ].type );
+
+				}
+
+			}
+
+			return element;
+
+		}
+
+		function parseASCII( data, header ) {
+
+			// PLY ascii format specification, as per http://en.wikipedia.org/wiki/PLY_(file_format)
+
+			var buffer = {
+				indices: [],
+				vertices: [],
+				normals: [],
+				uvs: [],
+				faceVertexUvs: [],
+				colors: []
+			};
+
+			var result;
+
+			var patternBody = /end_header\s([\s\S]*)$/;
+			var body = '';
+			if ( ( result = patternBody.exec( data ) ) !== null ) {
+
+				body = result[ 1 ];
+
+			}
+
+			var lines = body.split( '\n' );
+			var currentElement = 0;
+			var currentElementCount = 0;
+
+			for ( var i = 0; i < lines.length; i ++ ) {
+
+				var line = lines[ i ];
+				line = line.trim();
+				if ( line === '' ) {
+
+					continue;
+
+				}
+
+				if ( currentElementCount >= header.elements[ currentElement ].count ) {
+
+					currentElement ++;
+					currentElementCount = 0;
+
+				}
+
+				var element = parseASCIIElement( header.elements[ currentElement ].properties, line );
+
+				handleElement( buffer, header.elements[ currentElement ].name, element );
+
+				currentElementCount ++;
+
+			}
+
+			return postProcess( buffer );
+
+		}
+
+		function postProcess( buffer ) {
+
+			var geometry = new BufferGeometry();
+
+			// mandatory buffer data
+
+			if ( buffer.indices.length > 0 ) {
+
+				geometry.setIndex( buffer.indices );
+
+			}
+
+			geometry.addAttribute( 'position', new Float32BufferAttribute( buffer.vertices, 3 ) );
+
+			// optional buffer data
+
+			if ( buffer.normals.length > 0 ) {
+
+				geometry.addAttribute( 'normal', new Float32BufferAttribute( buffer.normals, 3 ) );
+
+			}
+
+			if ( buffer.uvs.length > 0 ) {
+
+				geometry.addAttribute( 'uv', new Float32BufferAttribute( buffer.uvs, 2 ) );
+
+			}
+
+			if ( buffer.colors.length > 0 ) {
+
+				geometry.addAttribute( 'color', new Float32BufferAttribute( buffer.colors, 3 ) );
+
+			}
+
+			if ( buffer.faceVertexUvs.length > 0 ) {
+
+				geometry = geometry.toNonIndexed();
+				geometry.addAttribute( 'uv', new Float32BufferAttribute( buffer.faceVertexUvs, 2 ) );
+
+			}
+
+			geometry.computeBoundingSphere();
+
+			return geometry;
+
+		}
+
+		function handleElement( buffer, elementName, element ) {
+
+			if ( elementName === 'vertex' ) {
+
+				buffer.vertices.push( element.x, element.y, element.z );
+
+				if ( 'nx' in element && 'ny' in element && 'nz' in element ) {
+
+					buffer.normals.push( element.nx, element.ny, element.nz );
+
+				}
+
+				if ( 's' in element && 't' in element ) {
+
+					buffer.uvs.push( element.s, element.t );
+
+				}
+
+				if ( 'red' in element && 'green' in element && 'blue' in element ) {
+
+					buffer.colors.push( element.red / 255.0, element.green / 255.0, element.blue / 255.0 );
+
+				}
+
+			} else if ( elementName === 'face' ) {
+
+				var vertex_indices = element.vertex_indices || element.vertex_index; // issue #9338
+				var texcoord = element.texcoord;
+
+				if ( vertex_indices.length === 3 ) {
+
+					buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 2 ] );
+
+					if ( texcoord && texcoord.length === 6 ) {
+
+						buffer.faceVertexUvs.push( texcoord[ 0 ], texcoord[ 1 ] );
+						buffer.faceVertexUvs.push( texcoord[ 2 ], texcoord[ 3 ] );
+						buffer.faceVertexUvs.push( texcoord[ 4 ], texcoord[ 5 ] );
+
+					}
+
+				} else if ( vertex_indices.length === 4 ) {
+
+					buffer.indices.push( vertex_indices[ 0 ], vertex_indices[ 1 ], vertex_indices[ 3 ] );
+					buffer.indices.push( vertex_indices[ 1 ], vertex_indices[ 2 ], vertex_indices[ 3 ] );
+
+				}
+
+			}
+
+		}
+
+		function binaryRead( dataview, at, type, little_endian ) {
+
+			switch ( type ) {
+
+				// corespondences for non-specific length types here match rply:
+				case 'int8':		case 'char':	 return [ dataview.getInt8( at ), 1 ];
+				case 'uint8':		case 'uchar':	 return [ dataview.getUint8( at ), 1 ];
+				case 'int16':		case 'short':	 return [ dataview.getInt16( at, little_endian ), 2 ];
+				case 'uint16':	case 'ushort': return [ dataview.getUint16( at, little_endian ), 2 ];
+				case 'int32':		case 'int':		 return [ dataview.getInt32( at, little_endian ), 4 ];
+				case 'uint32':	case 'uint':	 return [ dataview.getUint32( at, little_endian ), 4 ];
+				case 'float32': case 'float':	 return [ dataview.getFloat32( at, little_endian ), 4 ];
+				case 'float64': case 'double': return [ dataview.getFloat64( at, little_endian ), 8 ];
+
+			}
+
+		}
+
+		function binaryReadElement( dataview, at, properties, little_endian ) {
+
+			var element = {};
+			var result, read = 0;
+
+			for ( var i = 0; i < properties.length; i ++ ) {
+
+				if ( properties[ i ].type === 'list' ) {
+
+					var list = [];
+
+					result = binaryRead( dataview, at + read, properties[ i ].countType, little_endian );
+					var n = result[ 0 ];
+					read += result[ 1 ];
+
+					for ( var j = 0; j < n; j ++ ) {
+
+						result = binaryRead( dataview, at + read, properties[ i ].itemType, little_endian );
+						list.push( result[ 0 ] );
+						read += result[ 1 ];
+
+					}
+
+					element[ properties[ i ].name ] = list;
+
+				} else {
+
+					result = binaryRead( dataview, at + read, properties[ i ].type, little_endian );
+					element[ properties[ i ].name ] = result[ 0 ];
+					read += result[ 1 ];
+
+				}
+
+			}
+
+			return [ element, read ];
+
+		}
+
+		function parseBinary( data, header ) {
+
+			var buffer = {
+				indices: [],
+				vertices: [],
+				normals: [],
+				uvs: [],
+				faceVertexUvs: [],
+				colors: []
+			};
+
+			var little_endian = ( header.format === 'binary_little_endian' );
+			var body = new DataView( data, header.headerLength );
+			var result, loc = 0;
+
+			for ( var currentElement = 0; currentElement < header.elements.length; currentElement ++ ) {
+
+				for ( var currentElementCount = 0; currentElementCount < header.elements[ currentElement ].count; currentElementCount ++ ) {
+
+					result = binaryReadElement( body, loc, header.elements[ currentElement ].properties, little_endian );
+					loc += result[ 1 ];
+					var element = result[ 0 ];
+
+					handleElement( buffer, header.elements[ currentElement ].name, element );
+
+				}
+
+			}
+
+			return postProcess( buffer );
+
+		}
+
+		//
+
+		var geometry;
+		var scope = this;
+
+		if ( data instanceof ArrayBuffer ) {
+
+			var text = LoaderUtils.decodeText( new Uint8Array( data ) );
+			var header = parseHeader( text );
+
+			geometry = header.format === 'ascii' ? parseASCII( text, header ) : parseBinary( data, header );
+
+		} else {
+
+			geometry = parseASCII( data, parseHeader( data ) );
+
+		}
+
+		return geometry;
+
+	}
+
+};
+
+export { PLYLoader };

+ 16 - 0
examples/jsm/loaders/STLLoader.d.ts

@@ -0,0 +1,16 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+
+export class STLLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+  setPath(path: string) : this;
+
+  parse(data: ArrayBuffer | string) : BufferGeometry;
+}

+ 4 - 7
examples/jsm/loaders/STLLoader.js

@@ -18,15 +18,15 @@
  * Usage:
  * Usage:
  *  var loader = new STLLoader();
  *  var loader = new STLLoader();
  *  loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
  *  loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
- *    scene.add( new Mesh( geometry ) );
+ *    scene.add( new THREE.Mesh( geometry ) );
  *  });
  *  });
  *
  *
  * For binary STLs geometry might contain colors for vertices. To use it:
  * For binary STLs geometry might contain colors for vertices. To use it:
  *  // use the same code to load STL as above
  *  // use the same code to load STL as above
  *  if (geometry.hasColors) {
  *  if (geometry.hasColors) {
- *    material = new MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: VertexColors });
+ *    material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
  *  } else { .... }
  *  } else { .... }
- *  var mesh = new Mesh( geometry, material );
+ *  var mesh = new THREE.Mesh( geometry, material );
  */
  */
 
 
 import {
 import {
@@ -36,10 +36,7 @@ import {
 	FileLoader,
 	FileLoader,
 	Float32BufferAttribute,
 	Float32BufferAttribute,
 	LoaderUtils,
 	LoaderUtils,
-	Mesh,
-	MeshPhongMaterial,
-	Vector3,
-	VertexColors
+	Vector3
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
 
 

+ 16 - 0
examples/jsm/loaders/TGALoader.d.ts

@@ -0,0 +1,16 @@
+import {
+  Texture,
+  LoadingManager
+} from '../../../src/Three';
+
+
+export class TGALoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  load(url: string, onLoad: (texture: Texture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+  setPath(path: string) : this;
+
+  parse(data: ArrayBuffer) : Texture;
+}

+ 558 - 0
examples/jsm/loaders/TGALoader.js

@@ -0,0 +1,558 @@
+/*
+ * @author Daosheng Mu / https://github.com/DaoshengMu/
+ * @author mrdoob / http://mrdoob.com/
+ * @author takahirox / https://github.com/takahirox/
+ */
+
+import {
+	DefaultLoadingManager,
+	FileLoader,
+	Texture
+} from "../../../build/three.module.js";
+
+var TGALoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+};
+
+TGALoader.prototype = {
+
+	constructor: TGALoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var texture = new Texture();
+
+		var loader = new FileLoader( this.manager );
+		loader.setResponseType( 'arraybuffer' );
+		loader.setPath( this.path );
+
+		loader.load( url, function ( buffer ) {
+
+			texture.image = scope.parse( buffer );
+			texture.needsUpdate = true;
+
+			if ( onLoad !== undefined ) {
+
+				onLoad( texture );
+
+			}
+
+		}, onProgress, onError );
+
+		return texture;
+
+	},
+
+	parse: function ( buffer ) {
+
+		// reference from vthibault, https://github.com/vthibault/roBrowser/blob/master/src/Loaders/Targa.js
+
+		function tgaCheckHeader( header ) {
+
+			switch ( header.image_type ) {
+
+				// check indexed type
+
+				case TGA_TYPE_INDEXED:
+				case TGA_TYPE_RLE_INDEXED:
+					if ( header.colormap_length > 256 || header.colormap_size !== 24 || header.colormap_type !== 1 ) {
+
+						console.error( 'THREE.TGALoader: Invalid type colormap data for indexed type.' );
+
+					}
+					break;
+
+				// check colormap type
+
+				case TGA_TYPE_RGB:
+				case TGA_TYPE_GREY:
+				case TGA_TYPE_RLE_RGB:
+				case TGA_TYPE_RLE_GREY:
+					if ( header.colormap_type ) {
+
+						console.error( 'THREE.TGALoader: Invalid type colormap data for colormap type.' );
+
+					}
+					break;
+
+				// What the need of a file without data ?
+
+				case TGA_TYPE_NO_DATA:
+					console.error( 'THREE.TGALoader: No data.' );
+
+				// Invalid type ?
+
+				default:
+					console.error( 'THREE.TGALoader: Invalid type "%s".', header.image_type );
+
+			}
+
+			// check image width and height
+
+			if ( header.width <= 0 || header.height <= 0 ) {
+
+				console.error( 'THREE.TGALoader: Invalid image size.' );
+
+			}
+
+			// check image pixel size
+
+			if ( header.pixel_size !== 8 && header.pixel_size !== 16 &&
+				header.pixel_size !== 24 && header.pixel_size !== 32 ) {
+
+				console.error( 'THREE.TGALoader: Invalid pixel size "%s".', header.pixel_size );
+
+			}
+
+		}
+
+		// parse tga image buffer
+
+		function tgaParse( use_rle, use_pal, header, offset, data ) {
+
+			var pixel_data,
+				pixel_size,
+				pixel_total,
+				palettes;
+
+			pixel_size = header.pixel_size >> 3;
+			pixel_total = header.width * header.height * pixel_size;
+
+			 // read palettes
+
+			 if ( use_pal ) {
+
+				 palettes = data.subarray( offset, offset += header.colormap_length * ( header.colormap_size >> 3 ) );
+
+			 }
+
+			 // read RLE
+
+			 if ( use_rle ) {
+
+				 pixel_data = new Uint8Array( pixel_total );
+
+				var c, count, i;
+				var shift = 0;
+				var pixels = new Uint8Array( pixel_size );
+
+				while ( shift < pixel_total ) {
+
+					c = data[ offset ++ ];
+					count = ( c & 0x7f ) + 1;
+
+					// RLE pixels
+
+					if ( c & 0x80 ) {
+
+						// bind pixel tmp array
+
+						for ( i = 0; i < pixel_size; ++ i ) {
+
+							pixels[ i ] = data[ offset ++ ];
+
+						}
+
+						// copy pixel array
+
+						for ( i = 0; i < count; ++ i ) {
+
+							pixel_data.set( pixels, shift + i * pixel_size );
+
+						}
+
+						shift += pixel_size * count;
+
+					} else {
+
+						// raw pixels
+
+						count *= pixel_size;
+						for ( i = 0; i < count; ++ i ) {
+
+							pixel_data[ shift + i ] = data[ offset ++ ];
+
+						}
+						shift += count;
+
+					}
+
+				}
+
+			 } else {
+
+				// raw pixels
+
+				pixel_data = data.subarray(
+					 offset, offset += ( use_pal ? header.width * header.height : pixel_total )
+				);
+
+			 }
+
+			 return {
+				pixel_data: pixel_data,
+				palettes: palettes
+			 };
+
+		}
+
+		function tgaGetImageData8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image, palettes ) {
+
+			var colormap = palettes;
+			var color, i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i ++ ) {
+
+					color = image[ i ];
+					imageData[ ( x + width * y ) * 4 + 3 ] = 255;
+					imageData[ ( x + width * y ) * 4 + 2 ] = colormap[ ( color * 3 ) + 0 ];
+					imageData[ ( x + width * y ) * 4 + 1 ] = colormap[ ( color * 3 ) + 1 ];
+					imageData[ ( x + width * y ) * 4 + 0 ] = colormap[ ( color * 3 ) + 2 ];
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function tgaGetImageData16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var color, i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i += 2 ) {
+
+					color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ?
+					imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7;
+					imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2;
+					imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3;
+					imageData[ ( x + width * y ) * 4 + 3 ] = ( color & 0x8000 ) ? 0 : 255;
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function tgaGetImageData24bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i += 3 ) {
+
+					imageData[ ( x + width * y ) * 4 + 3 ] = 255;
+					imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ];
+					imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ];
+					imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ];
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function tgaGetImageData32bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i += 4 ) {
+
+					imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ];
+					imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 1 ];
+					imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 2 ];
+					imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 3 ];
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function tgaGetImageDataGrey8bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var color, i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i ++ ) {
+
+					color = image[ i ];
+					imageData[ ( x + width * y ) * 4 + 0 ] = color;
+					imageData[ ( x + width * y ) * 4 + 1 ] = color;
+					imageData[ ( x + width * y ) * 4 + 2 ] = color;
+					imageData[ ( x + width * y ) * 4 + 3 ] = 255;
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function tgaGetImageDataGrey16bits( imageData, y_start, y_step, y_end, x_start, x_step, x_end, image ) {
+
+			var i = 0, x, y;
+			var width = header.width;
+
+			for ( y = y_start; y !== y_end; y += y_step ) {
+
+				for ( x = x_start; x !== x_end; x += x_step, i += 2 ) {
+
+					imageData[ ( x + width * y ) * 4 + 0 ] = image[ i + 0 ];
+					imageData[ ( x + width * y ) * 4 + 1 ] = image[ i + 0 ];
+					imageData[ ( x + width * y ) * 4 + 2 ] = image[ i + 0 ];
+					imageData[ ( x + width * y ) * 4 + 3 ] = image[ i + 1 ];
+
+				}
+
+			}
+
+			return imageData;
+
+		}
+
+		function getTgaRGBA( data, width, height, image, palette ) {
+
+			var x_start,
+				y_start,
+				x_step,
+				y_step,
+				x_end,
+				y_end;
+
+			switch ( ( header.flags & TGA_ORIGIN_MASK ) >> TGA_ORIGIN_SHIFT ) {
+
+				default:
+				case TGA_ORIGIN_UL:
+					x_start = 0;
+					x_step = 1;
+					x_end = width;
+					y_start = 0;
+					y_step = 1;
+					y_end = height;
+					break;
+
+				case TGA_ORIGIN_BL:
+					x_start = 0;
+					x_step = 1;
+					x_end = width;
+					y_start = height - 1;
+					y_step = - 1;
+					y_end = - 1;
+					break;
+
+				case TGA_ORIGIN_UR:
+					x_start = width - 1;
+					x_step = - 1;
+					x_end = - 1;
+					y_start = 0;
+					y_step = 1;
+					y_end = height;
+					break;
+
+				case TGA_ORIGIN_BR:
+					x_start = width - 1;
+					x_step = - 1;
+					x_end = - 1;
+					y_start = height - 1;
+					y_step = - 1;
+					y_end = - 1;
+					break;
+
+			}
+
+			if ( use_grey ) {
+
+				switch ( header.pixel_size ) {
+
+					case 8:
+						tgaGetImageDataGrey8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					case 16:
+						tgaGetImageDataGrey16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					default:
+						console.error( 'THREE.TGALoader: Format not supported.' );
+						break;
+
+				}
+
+			} else {
+
+				switch ( header.pixel_size ) {
+
+					case 8:
+						tgaGetImageData8bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image, palette );
+						break;
+
+					case 16:
+						tgaGetImageData16bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					case 24:
+						tgaGetImageData24bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					case 32:
+						tgaGetImageData32bits( data, y_start, y_step, y_end, x_start, x_step, x_end, image );
+						break;
+
+					default:
+						console.error( 'THREE.TGALoader: Format not supported.' );
+						break;
+
+				}
+
+			}
+
+			// Load image data according to specific method
+			// var func = 'tgaGetImageData' + (use_grey ? 'Grey' : '') + (header.pixel_size) + 'bits';
+			// func(data, y_start, y_step, y_end, x_start, x_step, x_end, width, image, palette );
+			return data;
+
+		}
+
+		// TGA constants
+
+		var TGA_TYPE_NO_DATA = 0,
+			TGA_TYPE_INDEXED = 1,
+			TGA_TYPE_RGB = 2,
+			TGA_TYPE_GREY = 3,
+			TGA_TYPE_RLE_INDEXED = 9,
+			TGA_TYPE_RLE_RGB = 10,
+			TGA_TYPE_RLE_GREY = 11,
+
+			TGA_ORIGIN_MASK = 0x30,
+			TGA_ORIGIN_SHIFT = 0x04,
+			TGA_ORIGIN_BL = 0x00,
+			TGA_ORIGIN_BR = 0x01,
+			TGA_ORIGIN_UL = 0x02,
+			TGA_ORIGIN_UR = 0x03;
+
+		if ( buffer.length < 19 ) console.error( 'THREE.TGALoader: Not enough data to contain header.' );
+
+		var content = new Uint8Array( buffer ),
+			offset = 0,
+			header = {
+				id_length: content[ offset ++ ],
+				colormap_type: content[ offset ++ ],
+				image_type: content[ offset ++ ],
+				colormap_index: content[ offset ++ ] | content[ offset ++ ] << 8,
+				colormap_length: content[ offset ++ ] | content[ offset ++ ] << 8,
+				colormap_size: content[ offset ++ ],
+				origin: [
+					content[ offset ++ ] | content[ offset ++ ] << 8,
+					content[ offset ++ ] | content[ offset ++ ] << 8
+				],
+				width: content[ offset ++ ] | content[ offset ++ ] << 8,
+				height: content[ offset ++ ] | content[ offset ++ ] << 8,
+				pixel_size: content[ offset ++ ],
+				flags: content[ offset ++ ]
+			};
+
+			// check tga if it is valid format
+
+		tgaCheckHeader( header );
+
+		if ( header.id_length + offset > buffer.length ) {
+
+			console.error( 'THREE.TGALoader: No data.' );
+
+		}
+
+		// skip the needn't data
+
+		offset += header.id_length;
+
+		// get targa information about RLE compression and palette
+
+		var use_rle = false,
+			use_pal = false,
+			use_grey = false;
+
+		switch ( header.image_type ) {
+
+			case TGA_TYPE_RLE_INDEXED:
+				use_rle = true;
+				use_pal = true;
+				break;
+
+			case TGA_TYPE_INDEXED:
+				use_pal = true;
+				break;
+
+			case TGA_TYPE_RLE_RGB:
+				use_rle = true;
+				break;
+
+			case TGA_TYPE_RGB:
+				break;
+
+			case TGA_TYPE_RLE_GREY:
+				use_rle = true;
+				use_grey = true;
+				break;
+
+			case TGA_TYPE_GREY:
+				use_grey = true;
+				break;
+
+		}
+
+		//
+
+		var useOffscreen = typeof OffscreenCanvas !== 'undefined';
+
+		var canvas = useOffscreen ? new OffscreenCanvas( header.width, header.height ) : document.createElement( 'canvas' );
+		canvas.width = header.width;
+		canvas.height = header.height;
+
+		var context = canvas.getContext( '2d' );
+		var imageData = context.createImageData( header.width, header.height );
+
+		var result = tgaParse( use_rle, use_pal, header, offset, content );
+		var rgbaData = getTgaRGBA( imageData.data, header.width, header.height, result.pixel_data, result.palettes );
+
+		context.putImageData( imageData, 0, 0 );
+
+		return useOffscreen ? canvas.transferToImageBitmap() : canvas;
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	}
+
+};
+
+export { TGALoader };

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

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

+ 2 - 3
examples/misc_controls_fly.html

@@ -252,12 +252,11 @@
 				SCREEN_HEIGHT = window.innerHeight;
 				SCREEN_HEIGHT = window.innerHeight;
 				SCREEN_WIDTH = window.innerWidth;
 				SCREEN_WIDTH = window.innerWidth;
 
 
-				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-
 				camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
 				camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
-				composer.reset();
+				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
+				composer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 
 
 			}
 			}
 
 

BIN
examples/models/3mf/truck.3mf


BIN
examples/models/kmz/Box.kmz


BIN
examples/textures/memorial.exr


BIN
examples/textures/memorial.hdr


File diff suppressed because it is too large
+ 0 - 10
examples/textures/miranda_uncropped.hdr


+ 5 - 5
examples/webgl_buffergeometry_lines.html

@@ -50,7 +50,7 @@
 
 
 			var camera, scene, renderer;
 			var camera, scene, renderer;
 
 
-			var mesh;
+			var line;
 
 
 			init();
 			init();
 			animate();
 			animate();
@@ -99,8 +99,8 @@
 
 
 				geometry.computeBoundingSphere();
 				geometry.computeBoundingSphere();
 
 
-				mesh = new THREE.Line( geometry, material );
-				scene.add( mesh );
+				line = new THREE.Line( geometry, material );
+				scene.add( line );
 
 
 				//
 				//
 
 
@@ -148,8 +148,8 @@
 
 
 				var time = Date.now() * 0.001;
 				var time = Date.now() * 0.001;
 
 
-				mesh.rotation.x = time * 0.25;
-				mesh.rotation.y = time * 0.5;
+				line.rotation.x = time * 0.25;
+				line.rotation.y = time * 0.5;
 
 
 				renderer.render( scene, camera );
 				renderer.render( scene, camera );
 
 

+ 5 - 5
examples/webgl_buffergeometry_lines_indexed.html

@@ -50,7 +50,7 @@
 
 
 			var camera, scene, renderer;
 			var camera, scene, renderer;
 
 
-			var mesh, parent_node;
+			var parent_node;
 
 
 			init();
 			init();
 			animate();
 			animate();
@@ -201,12 +201,12 @@
 				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 				geometry.computeBoundingSphere();
 				geometry.computeBoundingSphere();
 
 
-				mesh = new THREE.LineSegments( geometry, material );
-				mesh.position.x -= 1200;
-				mesh.position.y -= 1200;
+				var lineSegments = new THREE.LineSegments( geometry, material );
+				lineSegments.position.x -= 1200;
+				lineSegments.position.y -= 1200;
 
 
 				parent_node = new THREE.Object3D();
 				parent_node = new THREE.Object3D();
-				parent_node.add( mesh );
+				parent_node.add( lineSegments );
 
 
 				scene.add( parent_node );
 				scene.add( parent_node );
 
 

+ 6 - 6
examples/webgl_custom_attributes_lines.html

@@ -83,7 +83,7 @@
 
 
 		var renderer, scene, camera, stats;
 		var renderer, scene, camera, stats;
 
 
-		var object, uniforms;
+		var line, uniforms;
 
 
 		var loader = new THREE.FontLoader();
 		var loader = new THREE.FontLoader();
 		loader.load( 'fonts/helvetiker_bold.typeface.json', function ( font ) {
 		loader.load( 'fonts/helvetiker_bold.typeface.json', function ( font ) {
@@ -155,9 +155,9 @@
 
 
 			}
 			}
 
 
-			object = new THREE.Line( geometry, shaderMaterial );
-			object.rotation.x = 0.2;
-			scene.add( object );
+			line = new THREE.Line( geometry, shaderMaterial );
+			line.rotation.x = 0.2;
+			scene.add( line );
 
 
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setPixelRatio( window.devicePixelRatio );
@@ -197,12 +197,12 @@
 
 
 			var time = Date.now() * 0.001;
 			var time = Date.now() * 0.001;
 
 
-			object.rotation.y = 0.25 * time;
+			line.rotation.y = 0.25 * time;
 
 
 			uniforms.amplitude.value = Math.sin( 0.5 * time );
 			uniforms.amplitude.value = Math.sin( 0.5 * time );
 			uniforms.color.value.offsetHSL( 0.0005, 0, 0 );
 			uniforms.color.value.offsetHSL( 0.0005, 0, 0 );
 
 
-			var attributes = object.geometry.attributes;
+			var attributes = line.geometry.attributes;
 			var array = attributes.displacement.array;
 			var array = attributes.displacement.array;
 
 
 			for ( var i = 0, l = array.length; i < l; i += 3 ) {
 			for ( var i = 0, l = array.length; i < l; i += 3 ) {

+ 158 - 0
examples/webgl_loader_3mf_materials.html

@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - 3MF</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #fff;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				background-color: #fff;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+			a {
+				color: #ff0000;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a>
+			<a href="http://3mf.io" target="_blank" rel="noopener">3MF file with materials</a>
+		</div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/loaders/3MFLoader.js"></script>
+
+		<script src="js/WebGL.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/libs/jszip.min.js"></script>
+
+		<script>
+
+			if ( WEBGL.isWebGLAvailable() === false ) {
+
+				document.body.appendChild( WEBGL.getWebGLErrorMessage() );
+
+			}
+
+			var camera, scene, renderer;
+
+			init();
+
+			function init() {
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xa0a0a0 );
+				scene.fog = new THREE.Fog( 0xa0a0a0, 10, 500 );
+
+				scene.add( new THREE.AmbientLight( 0xffffff, 0.2 ) );
+
+				camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 500 );
+				camera.position.set( - 50, 40, 50 );
+				scene.add( camera );
+
+				//
+
+				var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
+				hemiLight.position.set( 0, 100, 0 );
+				scene.add( hemiLight );
+
+				var dirLight = new THREE.DirectionalLight( 0xffffff );
+				dirLight.position.set( - 0, 40, 50 );
+				dirLight.castShadow = true;
+				dirLight.shadow.camera.top = 50;
+				dirLight.shadow.camera.bottom = - 25;
+				dirLight.shadow.camera.left = - 25;
+				dirLight.shadow.camera.right = 25;
+				dirLight.shadow.camera.near = 0.1;
+				dirLight.shadow.camera.far = 200;
+				dirLight.shadow.mapSize.set( 1024, 1024 );
+				scene.add( dirLight );
+
+				// scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) );
+
+				//
+
+				var loader = new THREE.ThreeMFLoader();
+				loader.load( './models/3mf/truck.3mf', function ( object ) {
+
+					object.quaternion.setFromEuler( new THREE.Euler( - Math.PI / 2, 0, 0 ) ); 	// z-up conversion
+
+					object.traverse( function ( child ) {
+
+						child.castShadow = true;
+
+					} );
+
+					scene.add( object );
+
+					render();
+
+				} );
+
+				//
+
+				var ground = new THREE.Mesh( new THREE.PlaneBufferGeometry( 1000, 1000 ), new THREE.MeshPhongMaterial( { color: 0x999999, depthWrite: false } ) );
+				ground.rotation.x = - Math.PI / 2;
+				ground.position.y = 11;
+				ground.receiveShadow = true;
+				scene.add( ground );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.gammaOutput = true;
+				renderer.gammaFactor = 2.2;
+				renderer.shadowMap.enabled = true;
+				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.addEventListener( 'change', render );
+				controls.minDistance = 50;
+				controls.maxDistance = 200;
+				controls.enablePan = false;
+				controls.target.set( 0, 20, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				render();
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				render();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 6 - 5
examples/webgl_loader_texture_exr.html

@@ -32,7 +32,8 @@
 	<body>
 	<body>
 
 
 		<div id="info">
 		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl EXR texture loader example
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl EXR texture loader example<br/>
+			Image courtesy of <a href="http://www.pauldebevec.com/Research/HDR/" target="_blank" rel="noopener">Paul Debevec</a>.
 		</div>
 		</div>
 
 
 		<script src="../build/three.js"></script>
 		<script src="../build/three.js"></script>
@@ -51,7 +52,7 @@
 			}
 			}
 
 
 			var params = {
 			var params = {
-				exposure: 1.0
+				exposure: 2.0
 			};
 			};
 
 
 			var renderer, scene, camera;
 			var renderer, scene, camera;
@@ -76,7 +77,7 @@
 
 
 				camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 1 );
 				camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 1 );
 
 
-				new THREE.EXRLoader().load( 'textures/piz_compressed.exr', function ( texture, textureData ) {
+				new THREE.EXRLoader().load( 'textures/memorial.exr', function ( texture, textureData ) {
 
 
 					//console.log( textureData );
 					//console.log( textureData );
 					//console.log( texture );
 					//console.log( texture );
@@ -92,7 +93,7 @@
 
 
 					var material = new THREE.MeshBasicMaterial( { map: texture } );
 					var material = new THREE.MeshBasicMaterial( { map: texture } );
 
 
-					var quad = new THREE.PlaneBufferGeometry( textureData.width / textureData.height, 1 );
+					var quad = new THREE.PlaneBufferGeometry( 1.5 * textureData.width / textureData.height, 1.5 );
 
 
 					var mesh = new THREE.Mesh( quad, material );
 					var mesh = new THREE.Mesh( quad, material );
 
 
@@ -106,7 +107,7 @@
 
 
 				var gui = new dat.GUI();
 				var gui = new dat.GUI();
 
 
-				gui.add( params, 'exposure', 0, 2 ).onChange( render );
+				gui.add( params, 'exposure', 0, 4, 0.01 ).onChange( render );
 				gui.open();
 				gui.open();
 
 
 				//
 				//

+ 6 - 5
examples/webgl_loader_texture_hdr.html

@@ -32,7 +32,8 @@
 	<body>
 	<body>
 
 
 		<div id="info">
 		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl HDR (RGBE) texture loader example
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl HDR (RGBE) texture loader example<br/>
+			Image courtesy of <a href="http://www.pauldebevec.com/Research/HDR/" target="_blank" rel="noopener">Paul Debevec</a>.
 		</div>
 		</div>
 
 
 		<script src="../build/three.js"></script>
 		<script src="../build/three.js"></script>
@@ -51,7 +52,7 @@
 			}
 			}
 
 
 			var params = {
 			var params = {
-				exposure: 1.0
+				exposure: 2.0
 			};
 			};
 
 
 			var renderer, scene, camera;
 			var renderer, scene, camera;
@@ -76,7 +77,7 @@
 
 
 				camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 1 );
 				camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 1 );
 
 
-				new THREE.RGBELoader().load( 'textures/miranda_uncropped.hdr', function ( texture, textureData ) {
+				new THREE.RGBELoader().load( 'textures/memorial.hdr', function ( texture, textureData ) {
 
 
 					//console.log( textureData );
 					//console.log( textureData );
 					//console.log( texture );
 					//console.log( texture );
@@ -88,7 +89,7 @@
 
 
 					var material = new THREE.MeshBasicMaterial( { map: texture } );
 					var material = new THREE.MeshBasicMaterial( { map: texture } );
 
 
-					var quad = new THREE.PlaneBufferGeometry( textureData.width / textureData.height, 1 );
+					var quad = new THREE.PlaneBufferGeometry( 1.5 * textureData.width / textureData.height, 1.5 );
 
 
 					var mesh = new THREE.Mesh( quad, material );
 					var mesh = new THREE.Mesh( quad, material );
 
 
@@ -102,7 +103,7 @@
 
 
 				var gui = new dat.GUI();
 				var gui = new dat.GUI();
 
 
-				gui.add( params, 'exposure', 0, 2 ).onChange( render );
+				gui.add( params, 'exposure', 0, 4, 0.01 ).onChange( render );
 				gui.open();
 				gui.open();
 
 
 				//
 				//

+ 1 - 1
examples/webgl_materials_video.html

@@ -244,7 +244,7 @@
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
-				composer.reset();
+				composer.setSize( window.innerWidth, window.innerHeight );
 
 
 			}
 			}
 
 

+ 6 - 7
examples/webgl_points_dynamic.html

@@ -135,8 +135,8 @@
 
 
 				effectFocus = new THREE.ShaderPass( THREE.FocusShader );
 				effectFocus = new THREE.ShaderPass( THREE.FocusShader );
 
 
-				effectFocus.uniforms[ "screenWidth" ].value = window.innerWidth;
-				effectFocus.uniforms[ "screenHeight" ].value = window.innerHeight;
+				effectFocus.uniforms[ "screenWidth" ].value = window.innerWidth * window.devicePixelRatio;
+				effectFocus.uniforms[ "screenHeight" ].value = window.innerHeight * window.devicePixelRatio;
 
 
 				composer = new THREE.EffectComposer( renderer );
 				composer = new THREE.EffectComposer( renderer );
 
 
@@ -156,17 +156,16 @@
 
 
 			function onWindowResize() {
 			function onWindowResize() {
 
 
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
 				camera.lookAt( scene.position );
 				camera.lookAt( scene.position );
 
 
-				composer.reset();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				composer.setSize( window.innerWidth, window.innerHeight );
 
 
-				effectFocus.uniforms[ "screenWidth" ].value = window.innerWidth;
-				effectFocus.uniforms[ "screenHeight" ].value = window.innerHeight;
+				effectFocus.uniforms[ "screenWidth" ].value = window.innerWidth * window.devicePixelRatio;
+				effectFocus.uniforms[ "screenHeight" ].value = window.innerHeight * window.devicePixelRatio;
 
 
 			}
 			}
 
 

+ 1 - 5
examples/webgl_postprocessing_backgrounds.html

@@ -239,11 +239,7 @@
 				cameraO.updateProjectionMatrix();*/
 				cameraO.updateProjectionMatrix();*/
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
-
-				var pixelRatio = renderer.getPixelRatio();
-				var newWidth = Math.floor( width / pixelRatio ) || 1;
-				var newHeight = Math.floor( height / pixelRatio ) || 1;
-				composer.setSize( newWidth, newHeight );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 

+ 17 - 2
examples/webgl_postprocessing_masking.html

@@ -37,7 +37,7 @@
 
 
 			}
 			}
 
 
-			var composer, renderer;
+			var camera, composer, renderer;
 			var box, torus;
 			var box, torus;
 
 
 			init();
 			init();
@@ -45,7 +45,7 @@
 
 
 			function init() {
 			function init() {
 
 
-				var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
 				camera.position.z = 10;
 				camera.position.z = 10;
 
 
 				var scene1 = new THREE.Scene();
 				var scene1 = new THREE.Scene();
@@ -101,6 +101,21 @@
 				composer.addPass( clearMaskPass );
 				composer.addPass( clearMaskPass );
 				composer.addPass( outputPass );
 				composer.addPass( outputPass );
 
 
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+				composer.setSize( width, height );
+
 			}
 			}
 
 
 			function animate() {
 			function animate() {

+ 1 - 5
examples/webgl_postprocessing_smaa.html

@@ -92,11 +92,7 @@
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
-
-				var pixelRatio = renderer.getPixelRatio();
-				var newWidth = Math.floor( width * pixelRatio ) || 1;
-				var newHeight = Math.floor( height * pixelRatio ) || 1;
-				composer.setSize( newWidth, newHeight );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 

+ 4 - 4
examples/webgl_postprocessing_sobel.html

@@ -118,8 +118,8 @@
 				// Sobel operator
 				// Sobel operator
 
 
 				effectSobel = new THREE.ShaderPass( THREE.SobelOperatorShader );
 				effectSobel = new THREE.ShaderPass( THREE.SobelOperatorShader );
-				effectSobel.uniforms[ "resolution" ].value.x = window.innerWidth;
-				effectSobel.uniforms[ "resolution" ].value.y = window.innerHeight;
+				effectSobel.uniforms[ 'resolution' ].value.x = window.innerWidth * window.devicePixelRatio;
+				effectSobel.uniforms[ 'resolution' ].value.y = window.innerHeight * window.devicePixelRatio;
 				composer.addPass( effectSobel );
 				composer.addPass( effectSobel );
 
 
 				var controls = new THREE.OrbitControls( camera, renderer.domElement );
 				var controls = new THREE.OrbitControls( camera, renderer.domElement );
@@ -145,8 +145,8 @@
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				composer.setSize( window.innerWidth, window.innerHeight );
 				composer.setSize( window.innerWidth, window.innerHeight );
 
 
-				effectSobel.uniforms[ "resolution" ].value.x = window.innerWidth;
-				effectSobel.uniforms[ "resolution" ].value.y = window.innerHeight;
+				effectSobel.uniforms[ 'resolution' ].value.x = window.innerWidth * window.devicePixelRatio;
+				effectSobel.uniforms[ 'resolution' ].value.y = window.innerHeight * window.devicePixelRatio;
 
 
 			}
 			}
 
 

+ 1 - 5
examples/webgl_postprocessing_ssaa.html

@@ -143,11 +143,7 @@
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
-
-				var pixelRatio = renderer.getPixelRatio();
-				var newWidth = Math.floor( width / pixelRatio ) || 1;
-				var newHeight = Math.floor( height / pixelRatio ) || 1;
-				composer.setSize( newWidth, newHeight );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 

+ 2 - 5
examples/webgl_postprocessing_ssaa_unbiased.html

@@ -177,6 +177,7 @@
 				// postprocessing
 				// postprocessing
 
 
 				composer = new THREE.EffectComposer( renderer );
 				composer = new THREE.EffectComposer( renderer );
+				composer.setPixelRatio( 1 ); // ensure pixel ratio is always 1 for performance reasons
 				ssaaRenderPassP = new THREE.SSAARenderPass( scene, cameraP );
 				ssaaRenderPassP = new THREE.SSAARenderPass( scene, cameraP );
 				composer.addPass( ssaaRenderPassP );
 				composer.addPass( ssaaRenderPassP );
 				ssaaRenderPassO = new THREE.SSAARenderPass( scene, cameraO );
 				ssaaRenderPassO = new THREE.SSAARenderPass( scene, cameraO );
@@ -204,11 +205,7 @@
 				cameraO.updateProjectionMatrix();
 				cameraO.updateProjectionMatrix();
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
-
-				var pixelRatio = renderer.getPixelRatio();
-				var newWidth = Math.floor( width / pixelRatio ) || 1;
-				var newHeight = Math.floor( height / pixelRatio ) || 1;
-				composer.setSize( newWidth, newHeight );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 

+ 8 - 11
examples/webgl_postprocessing_ssao.html

@@ -63,8 +63,7 @@
 
 
 			var container, stats;
 			var container, stats;
 			var camera, scene, renderer;
 			var camera, scene, renderer;
-			var effectComposer;
-			var ssaoPass;
+			var composer;
 			var group;
 			var group;
 
 
 			init();
 			init();
@@ -125,11 +124,11 @@
 				var width = window.innerWidth;
 				var width = window.innerWidth;
 				var height = window.innerHeight;
 				var height = window.innerHeight;
 
 
-				ssaoPass = new THREE.SSAOPass( scene, camera, width, height );
-				ssaoPass.kernelRadius = 16;
+				composer = new THREE.EffectComposer( renderer );
 
 
-				effectComposer = new THREE.EffectComposer( renderer );
-				effectComposer.addPass( ssaoPass );
+				var ssaoPass = new THREE.SSAOPass( scene, camera, width, height );
+				ssaoPass.kernelRadius = 16;
+				composer.addPass( ssaoPass );
 
 
 				// Init gui
 				// Init gui
 				var gui = new dat.GUI();
 				var gui = new dat.GUI();
@@ -152,8 +151,6 @@
 
 
 				window.addEventListener( 'resize', onWindowResize, false );
 				window.addEventListener( 'resize', onWindowResize, false );
 
 
-				onWindowResize();
-
 			}
 			}
 
 
 			function onWindowResize() {
 			function onWindowResize() {
@@ -163,9 +160,9 @@
 
 
 				camera.aspect = width / height;
 				camera.aspect = width / height;
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
-				renderer.setSize( width, height );
 
 
-				ssaoPass.setSize( width, height );
+				renderer.setSize( width, height );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 
@@ -185,7 +182,7 @@
 				group.rotation.x = timer * 0.0002;
 				group.rotation.x = timer * 0.0002;
 				group.rotation.y = timer * 0.0001;
 				group.rotation.y = timer * 0.0001;
 
 
-				effectComposer.render();
+				composer.render();
 
 
 			}
 			}
 
 

+ 1 - 5
examples/webgl_postprocessing_taa.html

@@ -172,11 +172,7 @@
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
-
-				var pixelRatio = renderer.getPixelRatio();
-				var newWidth = Math.floor( width / pixelRatio ) || 1;
-				var newHeight = Math.floor( height / pixelRatio ) || 1;
-				composer.setSize( newWidth, newHeight );
+				composer.setSize( width, height );
 
 
 			}
 			}
 
 

+ 0 - 1
examples/webgl_postprocessing_unreal_bloom.html

@@ -109,7 +109,6 @@
 			bloomPass.radius = params.bloomRadius;
 			bloomPass.radius = params.bloomRadius;
 
 
 			composer = new THREE.EffectComposer( renderer );
 			composer = new THREE.EffectComposer( renderer );
-			composer.setSize( window.innerWidth, window.innerHeight );
 			composer.addPass( renderScene );
 			composer.addPass( renderScene );
 			composer.addPass( bloomPass );
 			composer.addPass( bloomPass );
 
 

+ 2 - 4
examples/webgl_postprocessing_unreal_bloom_selective.html

@@ -140,7 +140,6 @@
 
 
 			var bloomComposer = new THREE.EffectComposer( renderer );
 			var bloomComposer = new THREE.EffectComposer( renderer );
 			bloomComposer.renderToScreen = false;
 			bloomComposer.renderToScreen = false;
-			bloomComposer.setSize( window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio );
 			bloomComposer.addPass( renderScene );
 			bloomComposer.addPass( renderScene );
 			bloomComposer.addPass( bloomPass );
 			bloomComposer.addPass( bloomPass );
 
 
@@ -158,7 +157,6 @@
 			finalPass.needsSwap = true;
 			finalPass.needsSwap = true;
 
 
 			var finalComposer = new THREE.EffectComposer( renderer );
 			var finalComposer = new THREE.EffectComposer( renderer );
-			finalComposer.setSize( window.innerWidth * window.devicePixelRatio, window.innerHeight * window.devicePixelRatio );
 			finalComposer.addPass( renderScene );
 			finalComposer.addPass( renderScene );
 			finalComposer.addPass( finalPass );
 			finalComposer.addPass( finalPass );
 
 
@@ -251,8 +249,8 @@
 
 
 				renderer.setSize( width, height );
 				renderer.setSize( width, height );
 
 
-				bloomComposer.setSize( width * window.devicePixelRatio, height * window.devicePixelRatio );
-				finalComposer.setSize( width * window.devicePixelRatio, height * window.devicePixelRatio );
+				bloomComposer.setSize( width, height );
+				finalComposer.setSize( width, height );
 
 
 				render();
 				render();
 
 

+ 2 - 3
examples/webgl_shader_lava.html

@@ -199,12 +199,11 @@
 
 
 			function onWindowResize() {
 			function onWindowResize() {
 
 
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.updateProjectionMatrix();
 				camera.updateProjectionMatrix();
 
 
-				composer.reset();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				composer.setSize( window.innerWidth, window.innerHeight );
 
 
 			}
 			}
 
 

+ 4 - 0
files/ic_close_black_24dp.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="#000000">
+    <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
+    <path d="M0 0h24v24H0z" fill="none"/>
+</svg>

+ 4 - 0
files/ic_code_black_24dp.svg

@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="#000000">
+    <path fill="none" d="M0 0h24v24H0V0z"/>
+    <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
+</svg>

+ 15 - 0
files/ic_github_black_24dp.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve" fill="#000000">
+<g id="XMLID_399_">
+	<path id="XMLID_290_" fill-rule="evenodd" clip-rule="evenodd" d="M12,2C6.48,2,2,6.59,2,12.25c0,4.53,2.87,8.37,6.84,9.73
+		c0.5,0.09,0.68-0.22,0.68-0.49c0-0.24-0.01-0.89-0.01-1.74c-2.78,0.62-3.37-1.37-3.37-1.37c-0.45-1.18-1.11-1.5-1.11-1.5
+		c-0.91-0.64,0.07-0.62,0.07-0.62c1,0.07,1.53,1.06,1.53,1.06c0.89,1.57,2.34,1.11,2.91,0.85c0.09-0.66,0.35-1.11,0.63-1.37
+		c-2.22-0.26-4.56-1.14-4.56-5.07c0-1.12,0.39-2.03,1.03-2.75c-0.1-0.26-0.45-1.3,0.1-2.71c0,0,0.84-0.28,2.75,1.05
+		c0.8-0.23,1.65-0.34,2.5-0.34c0.85,0,1.7,0.12,2.5,0.34c1.91-1.33,2.75-1.05,2.75-1.05c0.55,1.41,0.2,2.45,0.1,2.71
+		c0.64,0.72,1.03,1.63,1.03,2.75c0,3.94-2.34,4.81-4.57,5.06c0.36,0.32,0.68,0.94,0.68,1.9c0,1.37-0.01,2.48-0.01,2.81
+		c0,0.27,0.18,0.59,0.69,0.49c3.97-1.36,6.83-5.2,6.83-9.73C22,6.59,17.52,2,12,2"/>
+	<rect id="XMLID_289_" fill-rule="evenodd" clip-rule="evenodd" fill="none" width="24" height="24"/>
+</g>
+</svg>

+ 165 - 85
files/main.css

@@ -1,6 +1,14 @@
+:root {
+	--color-blue: #049EF4;
+	--text-color: #444;
+	--border-style: 1px solid #EEE;
+	--header-height: 48px;
+	--panel-padding: 16px;
+}
+
 @font-face {
 @font-face {
-	font-family: 'RobotoMono';
-	src: local('RobotoMono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
+	font-family: 'Roboto Mono';
+	src: local('Roboto Mono'), url('../files/RobotoMono-Regular.woff2') format('woff2');
 	font-weight: normal;
 	font-weight: normal;
 	font-style: normal;
 	font-style: normal;
 }
 }
@@ -16,13 +24,11 @@ html, body {
 body {
 body {
 	margin: 0px;
 	margin: 0px;
 	overflow: hidden;
 	overflow: hidden;
-
-	font-family: 'RobotoMono', monospace;
+	font-family: 'Roboto Mono', monospace;
 	font-size: 14px;
 	font-size: 14px;
-	line-height: 23px;
-
+	line-height: 24px;
 	background-color: #ffffff;
 	background-color: #ffffff;
-	color: #555;
+	color: var(--text-color);
 }
 }
 
 
 a {
 a {
@@ -30,107 +36,146 @@ a {
 }
 }
 
 
 h1 {
 h1 {
-	margin-top: 0px; /* reset */
-	margin-left: 15px;
-	margin-bottom: 20px;
-	padding-top: 13px;
-
 	font-size: 18px;
 	font-size: 18px;
-	font-weight: normal;
-}
-
-h1 a {
-	color: #049EF4;
+	font-weight: 500;
 }
 }
 
 
 h2 {
 h2 {
-	margin-top: 20px;
-
+	padding: 8px 0;
+	margin: 6px 0 12px 0;
 	font-size: 14px;
 	font-size: 14px;
 	font-weight: normal;
 	font-weight: normal;
-
-	color: #049EF4;
+	color: var(--color-blue);
 }
 }
 
 
 h3 {
 h3 {
-	margin: 20px 0 0 0;
-
+	margin: 4px 0;
 	font-size: 14px;
 	font-size: 14px;
 	line-height: 23px;
 	line-height: 23px;
 	font-weight: 500;
 	font-weight: 500;
 	text-transform: uppercase;
 	text-transform: uppercase;
-
 	color: #9E9E9E;
 	color: #9E9E9E;
 }
 }
 
 
+h1 a {
+	color: var(--color-blue);
+}
+
+#header {
+	display: flex;
+	height: 48px;
+	border-bottom: var(--border-style);
+	align-items: center;
+}
+#header h1 {
+	margin-left: 15px;
+	flex: 1;
+}
+
 #panel {
 #panel {
 	position: fixed;
 	position: fixed;
 	left: 0px;
 	left: 0px;
 	width: 300px;
 	width: 300px;
 	height: 100%;
 	height: 100%;
 	overflow: auto;
 	overflow: auto;
-
-	background: white;
-	border-right: 1px solid #f2f2f2;
+	border-right: var(--border-style);
+	display: flex;
+	flex-direction: column;
+	transition: 0s 0s height;
 }
 }
 
 
-	#panel #header {
-		position: -webkit-sticky;
-		position: sticky;
-		top: 0;
+	#panel #exitSearchButton {
+		position: absolute;
+		width: 40px;
+		height: 40px;
+		top: 4px;
+		right: calc(var(--panel-padding) - 12px);
+		z-index: 1000;
+		display: none;
+		background-size: 20px 20px;
+		background-position: 50% 50%;
+		background-repeat: no-repeat;
+		background-image: url(../files/ic_close_black_24dp.svg);
+		cursor: pointer;
+	}
 
 
-		background: white;
+	#panel.searchFocused #exitSearchButton {
+		display: block;
+	}
+
+	#panel.searchFocused #language {
+		display: none;
+	}
+
+	#panel.searchFocused #filter {
+		background-image: none;
+		padding-left: var(--panel-padding);
 	}
 	}
 
 
 	#panel #expandButton {
 	#panel #expandButton {
-		position: absolute;
-		right: 14px;
-		top: 14px;
+		margin-right: 14px;
+		margin-left: 4px;
 		display: none;
 		display: none;
 	}
 	}
 
 
 	#panel #sections {
 	#panel #sections {
-		font-size: 14px;
-		padding: 0px 16px;
+		font-weight: 500;
+		display: flex;
+		justify-content: center;
+		z-index: 1000;
+		position: relative;
+		height: 100%;
+		align-items: center;
 	}
 	}
 
 
 		#panel #sections * {
 		#panel #sections * {
-			display: inline-block;
-			margin-right: 35px;
-			margin-bottom: 12px;
-
-			font-weight: 500;
-
-			color: #333333;
+			padding: 0 var(--panel-padding);
+			height: 100%;
+			position: relative;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+		#panel #sections .selected:after {
+			content: "";
+			position: absolute;
+			left: 0;
+			right: 0;
+			bottom: -1px;
+			border-bottom: 1px solid black;
 		}
 		}
-
 		#panel #sections a {
 		#panel #sections a {
 			color: #9E9E9E;
 			color: #9E9E9E;
 		}
 		}
 
 
 	#panel #filter {
 	#panel #filter {
 		width: 100%;
 		width: 100%;
-		height: 40px;
-		padding: 0px 48px;
-		font-size: 15px;
+		height: 48px;
+		padding: 0px 44px;
+		font-weight: 500;
+		font-size: 14px;
+		color: var(--text-color);
 		outline: none;
 		outline: none;
-		border: 0px; /* reset */
-		border-top: 1px solid #f2f2f2;
-		border-bottom: 1px solid #f2f2f2;
-		background: url( "../files/ic_search_black_24dp.svg" ) no-repeat;
-		background-position: 14px center;
+		border: 0px;
+		border-bottom: var(--border-style);
+		background-size: 20px 20px;
+		background-image: url(../files/ic_search_black_24dp.svg);
+		background-position: 14px 50%;
+		background-repeat: no-repeat;
 	}
 	}
 
 
 	#panel #language {
 	#panel #language {
+		font-family: 'Roboto Mono', monospace;
 		position: absolute;
 		position: absolute;
-		top: 98px;
-		right: 14px;
+		top: 9px;
+		right: 8px;
 		border: 0px;
 		border: 0px;
 		font-size: 14px;
 		font-size: 14px;
-		background: url( "ic_arrow_drop_down_black_24dp.svg" ) no-repeat;
+		font-weight: 500;
+		background: url(ic_arrow_drop_down_black_24dp.svg) no-repeat;
 		background-position: right center;
 		background-position: right center;
 		background-color: white;
 		background-color: white;
-		padding: 4px 26px 4px 8px;
+		padding: 4px 24px 4px 8px;
 		-webkit-appearance: none;
 		-webkit-appearance: none;
 		-moz-appearance: none;
 		-moz-appearance: none;
 		appearance: none;
 		appearance: none;
@@ -140,26 +185,39 @@ h3 {
 			outline: none;
 			outline: none;
 		}
 		}
 
 
+	#contentWrapper {
+		flex: 1;
+		overflow: hidden;
+		display: flex;
+		flex-direction: column;
+		transform: translate3d(0,0,0);
+	}
 	#panel #content {
 	#panel #content {
-		margin: 0px 16px;
+		flex: 1;
+		overflow-y: scroll;
+		padding: 0 var(--panel-padding) 24px var(--panel-padding);
 	}
 	}
 
 
 		#panel #content ul {
 		#panel #content ul {
 			list-style-type: none;
 			list-style-type: none;
 			padding: 0px;
 			padding: 0px;
-			margin: 0px;
+			margin: 0px 0 24px 0;
+		}
+		#panel #content ul li {
+			margin: 2px 0;
 		}
 		}
 
 
 		#panel #content a {
 		#panel #content a {
-			color: #222222;
+			color: var(--text-color);
 		}
 		}
 
 
-			#panel #content a:hover {
-				text-decoration: underline;
-			}
+		#panel #content a:hover,
+		#panel #content .selected {
+			color: var(--color-blue);
+		}
 
 
 		#panel #content .selected {
 		#panel #content .selected {
-			color: #ff0000;
+			text-decoration: underline;
 		}
 		}
 
 
 		#panel #content .hidden {
 		#panel #content .hidden {
@@ -178,45 +236,67 @@ iframe {
 /* mobile */
 /* mobile */
 
 
 @media all and ( max-width: 640px ) {
 @media all and ( max-width: 640px ) {
-
+	#panel #expandButton {
+		display: block;
+	}
 	#panel {
 	#panel {
 		position: absolute;
 		position: absolute;
 		left: 0;
 		left: 0;
 		top: 0;
 		top: 0;
-		height: 100%;
 		width: 100%;
 		width: 100%;
 		right: 0;
 		right: 0;
 		z-index: 100;
 		z-index: 100;
-		border-bottom: 1px solid #dedede;
+		overflow-x: hidden;
+		transition: 0s 0s height;
+		border: none;
+		height: var(--header-height);
+		transition: 0s 0.2s height;
+	}
+	#panel.open {
+		height: 100%;
+		transition: 0s 0s height;
 	}
 	}
 
 
-		#panel #expandButton {
-			display: block;
-		}
-
-	/*
-	#navigation {
+	#panelScrim {
+		pointer-events: none;
+		background-color: rgba(0,0,0,0);
 		position: absolute;
 		position: absolute;
 		left: 0;
 		left: 0;
-		top: 90px;
 		right: 0;
 		right: 0;
+		top: 0;
 		bottom: 0;
 		bottom: 0;
-		font-size: 17px;
-		line-height: 22px;
-		overflow: auto;
+		z-index: 1000;
+		pointer-events: none;
+		transition: .2s background-color;
+	}
+	#panel.open #panelScrim {
+		pointer-events: auto;
+		background-color: rgba(0,0,0,0.4);
+	}
+
+	#contentWrapper {
+		position: absolute;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		background: white;
+		box-shadow: 0 0 8px rgba(0,0,0,.1);
+		width: calc(100vw - 60px);
+		max-width: 360px;
+		z-index: 10000;
+		transition: .25s transform;
+		overflow-x: hidden;
+		margin-right: -380px;
+	}
+	#panel.open #contentWrapper {
+		transform: translate3d(-380px, 0 ,0);
 	}
 	}
-	*/
 
 
 	iframe {
 	iframe {
 		position: absolute;
 		position: absolute;
 		left: 0;
 		left: 0;
-		top: 92px;
+		top: var(--header-height);
 		width: 100%;
 		width: 100%;
-		height: calc(100% - 92px);
-	}
-
-	#panel.collapsed {
-		height: 92px;
+		height: calc(100% - var(--header-height));
 	}
 	}
-
 }
 }

+ 17 - 3
src/animation/AnimationAction.js

@@ -503,11 +503,19 @@ Object.assign( AnimationAction.prototype, {
 
 
 					time = 0;
 					time = 0;
 
 
-				} else break handle_stop;
+				} else {
+
+					this.time = time;
+
+					break handle_stop;
+
+				}
 
 
 				if ( this.clampWhenFinished ) this.paused = true;
 				if ( this.clampWhenFinished ) this.paused = true;
 				else this.enabled = false;
 				else this.enabled = false;
 
 
+				this.time = time;
+
 				this._mixer.dispatchEvent( {
 				this._mixer.dispatchEvent( {
 					type: 'finished', action: this,
 					type: 'finished', action: this,
 					direction: deltaTime < 0 ? - 1 : 1
 					direction: deltaTime < 0 ? - 1 : 1
@@ -559,6 +567,8 @@ Object.assign( AnimationAction.prototype, {
 
 
 					time = deltaTime > 0 ? duration : 0;
 					time = deltaTime > 0 ? duration : 0;
 
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						type: 'finished', action: this,
 						direction: deltaTime > 0 ? 1 : - 1
 						direction: deltaTime > 0 ? 1 : - 1
@@ -583,26 +593,30 @@ Object.assign( AnimationAction.prototype, {
 
 
 					this._loopCount = loopCount;
 					this._loopCount = loopCount;
 
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 					this._mixer.dispatchEvent( {
 						type: 'loop', action: this, loopDelta: loopDelta
 						type: 'loop', action: this, loopDelta: loopDelta
 					} );
 					} );
 
 
 				}
 				}
 
 
+			} else {
+
+				this.time = time;
+
 			}
 			}
 
 
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 
 				// invert time for the "pong round"
 				// invert time for the "pong round"
 
 
-				this.time = time;
 				return duration - time;
 				return duration - time;
 
 
 			}
 			}
 
 
 		}
 		}
 
 
-		this.time = time;
 		return time;
 		return time;
 
 
 	},
 	},

+ 11 - 0
src/core/BufferAttribute.js

@@ -319,6 +319,17 @@ Object.assign( BufferAttribute.prototype, {
 
 
 		return new this.constructor( this.array, this.itemSize ).copy( this );
 		return new this.constructor( this.array, this.itemSize ).copy( this );
 
 
+	},
+
+	toJSON: function () {
+
+		return {
+			itemSize: this.itemSize,
+			type: this.array.constructor.name,
+			array: Array.prototype.slice.call( this.array ),
+			normalized: this.normalized
+		};
+
 	}
 	}
 
 
 } );
 } );

+ 2 - 12
src/core/BufferGeometry.js

@@ -1063,12 +1063,7 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 
 
 			var attribute = attributes[ key ];
 			var attribute = attributes[ key ];
 
 
-			var attributeData = {
-				itemSize: attribute.itemSize,
-				type: attribute.array.constructor.name,
-				array: Array.prototype.slice.call( attribute.array ),
-				normalized: attribute.normalized
-			};
+			var attributeData = attribute.toJSON();
 
 
 			if ( attribute.name !== '' ) attributeData.name = attribute.name;
 			if ( attribute.name !== '' ) attributeData.name = attribute.name;
 
 
@@ -1089,12 +1084,7 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 
 
 				var attribute = attributeArray[ i ];
 				var attribute = attributeArray[ i ];
 
 
-				var attributeData = {
-					itemSize: attribute.itemSize,
-					type: attribute.array.constructor.name,
-					array: Array.prototype.slice.call( attribute.array ),
-					normalized: attribute.normalized
-				};
+				var attributeData = attribute.toJSON();
 
 
 				if ( attribute.name !== '' ) attributeData.name = attribute.name;
 				if ( attribute.name !== '' ) attributeData.name = attribute.name;
 
 

+ 12 - 0
src/core/InstancedBufferAttribute.js

@@ -36,6 +36,18 @@ InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribu
 
 
 		return this;
 		return this;
 
 
+	},
+
+	toJSON: function ()	{
+
+		var data = BufferAttribute.prototype.toJSON.call( this );
+
+		data.meshPerAttribute = this.meshPerAttribute;
+
+		data.isInstancedBufferAttribute = true;
+
+		return data;
+
 	}
 	}
 
 
 } );
 } );

+ 1 - 0
src/geometries/ExtrudeGeometry.d.ts

@@ -13,6 +13,7 @@ export interface ExtrudeGeometryOptions {
   bevelEnabled?: boolean;
   bevelEnabled?: boolean;
   bevelThickness?: number;
   bevelThickness?: number;
   bevelSize?: number;
   bevelSize?: number;
+  bevelOffset?: number;
   bevelSegments?: number;
   bevelSegments?: number;
   extrudePath?: CurvePath<Vector3>;
   extrudePath?: CurvePath<Vector3>;
   UVGenerator?: UVGenerator;
   UVGenerator?: UVGenerator;

+ 3 - 0
src/geometries/TextGeometry.d.ts

@@ -10,6 +10,7 @@ export interface TextGeometryParameters {
   bevelEnabled?: boolean;
   bevelEnabled?: boolean;
   bevelThickness?: number;
   bevelThickness?: number;
   bevelSize?: number;
   bevelSize?: number;
+  bevelOffset?: number;
   bevelSegments?: number;
   bevelSegments?: number;
 }
 }
 
 
@@ -24,6 +25,7 @@ export class TextBufferGeometry extends ExtrudeBufferGeometry {
     bevelEnabled: boolean;
     bevelEnabled: boolean;
     bevelThickness: number;
     bevelThickness: number;
     bevelSize: number;
     bevelSize: number;
+    bevelOffset: number;
     bevelSegments: number;
     bevelSegments: number;
   };
   };
 }
 }
@@ -39,6 +41,7 @@ export class TextGeometry extends ExtrudeGeometry {
     bevelEnabled: boolean;
     bevelEnabled: boolean;
     bevelThickness: number;
     bevelThickness: number;
     bevelSize: number;
     bevelSize: number;
+    bevelOffset: number;
     bevelSegments: number;
     bevelSegments: number;
   };
   };
 }
 }

+ 1 - 0
src/materials/Material.d.ts

@@ -44,6 +44,7 @@ export interface MaterialParameters {
   dithering?: boolean;
   dithering?: boolean;
   flatShading?: boolean;
   flatShading?: boolean;
   side?: Side;
   side?: Side;
+	shadowSide?: Side;
   transparent?: boolean;
   transparent?: boolean;
   vertexColors?: Colors;
   vertexColors?: Colors;
   vertexTangents?: boolean;
   vertexTangents?: boolean;

+ 1 - 1
src/renderers/WebGLRenderer.js

@@ -84,7 +84,7 @@ function WebGLRenderer( parameters ) {
 		 * Enables error checking and reporting when shader programs are being compiled
 		 * Enables error checking and reporting when shader programs are being compiled
 		 * @type {boolean}
 		 * @type {boolean}
 		 */
 		 */
-		checkShaderErrors: false
+		checkShaderErrors: true
 	};
 	};
 
 
 	// clearing
 	// clearing

+ 9 - 0
src/renderers/webgl/WebGLAnimation.d.ts

@@ -0,0 +1,9 @@
+export class WebGLAnimation {
+	start(): void;
+
+	stop(): void;
+
+	setAnimationLoop(callback: Function): void;
+
+	setContext(value: CanvasRenderingContext2D | WebGLRenderingContext): void;
+}

+ 9 - 0
src/renderers/webgl/WebGLAttributes.d.ts

@@ -0,0 +1,9 @@
+export class WebGLAttributes {
+	constructor(gl: CanvasRenderingContext2D | WebGLRenderingContext);
+
+	get(attribute: any): any;
+
+	remove(attribute: any): void;
+
+	update(attribute: any, bufferType: Array): void;
+}

+ 17 - 0
src/renderers/webgl/WebGLBackground.d.ts

@@ -0,0 +1,17 @@
+
+import { Color } from "../../math/Color.js";
+import { WebGLRenderer } from "../WebGLRenderer.js"
+import { WebGLState } from "./WebGLState.js"
+import { WebGLObjects } from "./WebGLObjects.js"
+import { WebGLRenderLists } from "./WebGLRenderLists.js"
+import { Scene } from "../../scenes/Scene.js"
+
+export class WebGLBackground {
+	constructor(renderer: WebGLRenderer, state: WebGLState, objects: WebGLObjects, premultipliedAlpha: any);
+
+	getClearColor(): void;
+	setClearColor(color: Color, alpha: any): void;
+	getClearAlpha(): void;
+	setClearAlpha(alpha: any): void;
+	render(renderList: WebGLRenderLists, scene: Scene, camera: any, forceClear: any): void;
+}

+ 5 - 0
src/renderers/webgl/WebGLUtils.d.ts

@@ -0,0 +1,5 @@
+export class WebGLUtils {
+	constructor(gl: CanvasRenderingContext2D | WebGLRenderingContext, extensions: any, capabilities: any);
+
+	convert(p: any): void;
+}

+ 2 - 0
src/renderers/webvr/WebVRManager.js

@@ -153,6 +153,8 @@ function WebVRManager( renderer ) {
 
 
 				var buttonId = gamepad.id === 'Daydream Controller' ? 0 : 1;
 				var buttonId = gamepad.id === 'Daydream Controller' ? 0 : 1;
 
 
+				if ( triggers[ i ] === undefined ) triggers[ i ] = false;
+
 				if ( triggers[ i ] !== gamepad.buttons[ buttonId ].pressed ) {
 				if ( triggers[ i ] !== gamepad.buttons[ buttonId ].pressed ) {
 
 
 					triggers[ i ] = gamepad.buttons[ buttonId ].pressed;
 					triggers[ i ] = gamepad.buttons[ buttonId ].pressed;

+ 9 - 1
utils/modularize.js

@@ -8,8 +8,12 @@ var srcFolder = __dirname + '/../examples/js/';
 var dstFolder = __dirname + '/../examples/jsm/';
 var dstFolder = __dirname + '/../examples/jsm/';
 
 
 var files = [
 var files = [
+	{ path: 'controls/DragControls.js', ignoreList: [] },
+	{ path: 'controls/DeviceOrientationControls.js', ignoreList: [] },
+	{ path: 'controls/EditorControls.js', ignoreList: [] },
 	{ path: 'controls/OrbitControls.js', ignoreList: [] },
 	{ path: 'controls/OrbitControls.js', ignoreList: [] },
 	{ path: 'controls/MapControls.js', ignoreList: [] },
 	{ path: 'controls/MapControls.js', ignoreList: [] },
+	{ path: 'controls/PointerLockControls.js', ignoreList: [] },
 	{ path: 'controls/TrackballControls.js', ignoreList: [] },
 	{ path: 'controls/TrackballControls.js', ignoreList: [] },
 	// { path: 'controls/TransformControls.js', ignoreList: [] },
 	// { path: 'controls/TransformControls.js', ignoreList: [] },
 
 
@@ -20,10 +24,14 @@ var files = [
 	{ path: 'exporters/STLExporter.js', ignoreList: [] },
 	{ path: 'exporters/STLExporter.js', ignoreList: [] },
 	{ path: 'exporters/TypedGeometryExporter.js', ignoreList: [] },
 	{ path: 'exporters/TypedGeometryExporter.js', ignoreList: [] },
 
 
+	{ path: 'loaders/BVHLoader.js', ignoreList: [ 'Bones' ] },
+	{ path: 'loaders/PCDLoader.js', ignoreList: [] },
 	{ path: 'loaders/GLTFLoader.js', ignoreList: [ 'NoSide', 'Matrix2', 'DDSLoader' ] },
 	{ path: 'loaders/GLTFLoader.js', ignoreList: [ 'NoSide', 'Matrix2', 'DDSLoader' ] },
 	{ path: 'loaders/OBJLoader.js', ignoreList: [] },
 	{ path: 'loaders/OBJLoader.js', ignoreList: [] },
 	{ path: 'loaders/MTLLoader.js', ignoreList: [] },
 	{ path: 'loaders/MTLLoader.js', ignoreList: [] },
-	{ path: 'loaders/STLLoader.js', ignoreList: [] },
+	{ path: 'loaders/PLYLoader.js', ignoreList: [ 'Mesh' ] },
+	{ path: 'loaders/STLLoader.js', ignoreList: [ 'Mesh', 'MeshPhongMaterial', 'VertexColors' ] },
+	{ path: 'loaders/TGALoader.js', ignoreList: [] },
 
 
 	{ path: 'pmrem/PMREMCubeUVPacker.js', ignoreList: [] },
 	{ path: 'pmrem/PMREMCubeUVPacker.js', ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', ignoreList: [] },
 	{ path: 'pmrem/PMREMGenerator.js', ignoreList: [] },

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