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 ) {
 
+		var renderWidth, renderHeight;
 		var scope = this;
 
 		var device = null;
@@ -21998,11 +21999,11 @@
 		var tempPosition = new Vector3();
 
 		var cameraL = new PerspectiveCamera();
-		cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );
+		cameraL.viewport = new Vector4();
 		cameraL.layers.enable( 1 );
 
 		var cameraR = new PerspectiveCamera();
-		cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );
+		cameraR.viewport = new Vector4();
 		cameraR.layers.enable( 2 );
 
 		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
@@ -22024,14 +22025,17 @@
 			if ( isPresenting() ) {
 
 				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();
 				renderer.getSize( currentSize );
 
 				renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 
+				cameraL.viewport.set( 0, 0, renderWidth, renderHeight );
+				cameraR.viewport.set( renderWidth, 0, renderWidth, renderHeight );
+
 				animation.start();
 
 			} 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;
@@ -22299,17 +22313,8 @@
 
 				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
 			 * @type {boolean}
 			 */
-			checkShaderErrors: false
+			checkShaderErrors: true
 		};
 
 		// clearing
@@ -23998,22 +24003,7 @@
 
 						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 );
 
@@ -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;
 
-					} else break handle_stop;
+					} else {
+
+						this.time = time;
+
+						break handle_stop;
+
+					}
 
 					if ( this.clampWhenFinished ) this.paused = true;
 					else this.enabled = false;
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						direction: deltaTime < 0 ? - 1 : 1
@@ -43300,6 +43307,8 @@
 
 						time = deltaTime > 0 ? duration : 0;
 
+						this.time = time;
+
 						this._mixer.dispatchEvent( {
 							type: 'finished', action: this,
 							direction: deltaTime > 0 ? 1 : - 1
@@ -43324,26 +43333,30 @@
 
 						this._loopCount = loopCount;
 
+						this.time = time;
+
 						this._mixer.dispatchEvent( {
 							type: 'loop', action: this, loopDelta: loopDelta
 						} );
 
 					}
 
+				} else {
+
+					this.time = time;
+
 				}
 
 				if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 					// invert time for the "pong round"
 
-					this.time = time;
 					return duration - time;
 
 				}
 
 			}
 
-			this.time = 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 ) {
 
+	var renderWidth, renderHeight;
 	var scope = this;
 
 	var device = null;
@@ -21992,11 +21993,11 @@ function WebVRManager( renderer ) {
 	var tempPosition = new Vector3();
 
 	var cameraL = new PerspectiveCamera();
-	cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );
+	cameraL.viewport = new Vector4();
 	cameraL.layers.enable( 1 );
 
 	var cameraR = new PerspectiveCamera();
-	cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );
+	cameraR.viewport = new Vector4();
 	cameraR.layers.enable( 2 );
 
 	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
@@ -22018,14 +22019,17 @@ function WebVRManager( renderer ) {
 		if ( isPresenting() ) {
 
 			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();
 			renderer.getSize( currentSize );
 
 			renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
 
+			cameraL.viewport.set( 0, 0, renderWidth, renderHeight );
+			cameraR.viewport.set( renderWidth, 0, renderWidth, renderHeight );
+
 			animation.start();
 
 		} 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;
@@ -22293,17 +22307,8 @@ function WebVRManager( renderer ) {
 
 			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
 		 * @type {boolean}
 		 */
-		checkShaderErrors: false
+		checkShaderErrors: true
 	};
 
 	// clearing
@@ -23992,22 +23997,7 @@ function WebGLRenderer( parameters ) {
 
 					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 );
 
@@ -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;
 
-				} else break handle_stop;
+				} else {
+
+					this.time = time;
+
+					break handle_stop;
+
+				}
 
 				if ( this.clampWhenFinished ) this.paused = true;
 				else this.enabled = false;
 
+				this.time = time;
+
 				this._mixer.dispatchEvent( {
 					type: 'finished', action: this,
 					direction: deltaTime < 0 ? - 1 : 1
@@ -43294,6 +43301,8 @@ Object.assign( AnimationAction.prototype, {
 
 					time = deltaTime > 0 ? duration : 0;
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						direction: deltaTime > 0 ? 1 : - 1
@@ -43318,26 +43327,30 @@ Object.assign( AnimationAction.prototype, {
 
 					this._loopCount = loopCount;
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 						type: 'loop', action: this, loopDelta: loopDelta
 					} );
 
 				}
 
+			} else {
+
+				this.time = time;
+
 			}
 
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 				// invert time for the "pong round"
 
-				this.time = time;
 				return duration - time;
 
 			}
 
 		}
 
-		this.time = time;
 		return time;
 
 	},

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

@@ -425,7 +425,7 @@
 
 		<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>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>
 		<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>
 	</head>
 	<body>
-
-		<div id="panel" class="collapsed">
+		<div id="panel" class="">
 
 			<div id="header">
-
 				<h1><a href="http://threejs.org">three.js</a></h1>
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
-
 				<div id="sections">
-					<span class="selected">docs</span> <a href="../examples/">examples</a>
+					<span class="selected">docs</span>
+					<a href="../examples/">examples</a>
 				</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">
 					<option value="en">en</option>
 					<option value="zh">zh</option>
 				</select>
 
+				<div id="content"></div>
 			</div>
 
-			<div id="content"></div>
-
 		</div>
 
 		<iframe name="viewer"></iframe>
@@ -89,6 +92,8 @@
 			var panel = document.getElementById( 'panel' );
 			var content = document.getElementById( 'content' );
 			var expandButton = document.getElementById( 'expandButton' );
+			var exitSearchButton = document.getElementById( 'exitSearchButton' );
+			var panelScrim = document.getElementById( 'panelScrim' );
 			var filterInput = document.getElementById( 'filter' );
 			var iframe = document.querySelector( 'iframe' );
 
@@ -104,12 +109,40 @@
 			expandButton.onclick = function ( event ) {
 
 				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
+			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 ) {
 
@@ -139,7 +172,16 @@
 					if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
 
 					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>
 
-		<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>
 			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>
 				<li>controls
 					<ul>
+						<li>DeviceOrientationControls</li>
+						<li>DragControls</li>
+						<li>EditorControls</li>
 						<li>MapControls</li>
 						<li>OrbitControls</li>
+						<li>PointerLockControls</li>
 						<li>TrackballControls</li>
 					</ul>
 				</li>
@@ -96,10 +100,14 @@
 				</li>
 				<li>loaders
 					<ul>
+						<li>BVHLoader</li>
 						<li>GLTFLoader</li>
 						<li>MTLLoader</li>
 						<li>OBJLoader</li>
+						<li>PCDLoader</li>
+						<li>PLYLoader</li>
 						<li>STLLoader</li>
+						<li>TGALoader</li>
 					</ul>
 				</li>
 				<li>pmrem
@@ -124,7 +132,7 @@
 		</p>
 		<p>
 			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
 			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.

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

@@ -25,7 +25,7 @@
 
 		<h2>Help forums</h2>
 		<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.
 		</p>
 
@@ -46,6 +46,9 @@
 
 		<h3>More extensive / advanced articles and courses</h3>
 		<ul>
+			<li>
+				[link:https://discoverthreejs.com/ Discover three.js]
+			</li>
 			<li>
 				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
 			</li>
@@ -66,21 +69,16 @@
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 		 </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>
 		</ul>
 
-		<h3>Tutorials in other languages</h3>
+		<h2>News and Updates</h2>
 		<ul>
 			<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>
-
-		</ul>
-
-		<h2>News and Updates</h2>
-		<ul>
 			<li>
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 			</li>
@@ -90,9 +88,6 @@
 			<li>
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 			</li>
-			<li>
-				[link:https://plus.google.com/104300307601542851567/posts Three.js posts] on Google+ – frequent posts on Three.js
-			</li>
 		</ul>
 
 		<h2>Examples</h2>
@@ -179,6 +174,13 @@
 			[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]
 		</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>
 
 	</body>

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

@@ -31,6 +31,9 @@
 
 		<h3>three.js入门</h3>
 		<ul>
+			<li>
+				[link:https://threejsfundamentals.org/threejs/lessons/threejs-fundamentals.html Three.js Fundamentals starting lesson]
+			</li>
 			<li>
 				[link:https://codepen.io/rachsmith/post/beginning-with-3d-webgl-pt-1-the-scene Beginning with 3D WebGL] by [link:https://codepen.io/rachsmith/ Rachel Smith].
 			</li>
@@ -41,6 +44,12 @@
 
 		<h3>更加广泛、高级的文章与教程</h3>
 		<ul>
+			<li>
+				[link:https://discoverthreejs.com/ Discover three.js]
+			</li>
+			<li>
+				[link:https://threejsfundamentals.org/ Three.js Fundamentals]
+			</li>
 			<li>
 				[link:http://blog.cjgammon.com/ Collection of tutorials] by [link:http://www.cjgammon.com/ CJ Gammon].
 			</li>
@@ -58,21 +67,16 @@
 			 [link:http://learningthreejs.com/ Learning Three.js] – a blog with articles dedicated to teaching three.js
 		 </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>
 		</ul>
 
-		<h3>其它非英语的教程</h3>
+		<h2>新闻与更新</h2>
 		<ul>
 			<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>
-
-		</ul>
-
-		<h2>新闻与更新</h2>
-		<ul>
 			<li>
 				[link:http://www.reddit.com/r/threejs/ Three.js on reddit]
 			</li>
@@ -82,9 +86,6 @@
 			<li>
 				[link:http://learningwebgl.com/blog/ Learning WebGL Blog] – The authoritive news source for WebGL.
 			</li>
-			<li>
-				[link:https://plus.google.com/104300307601542851567/posts Three.js posts] on Google+ – frequent posts on Three.js
-			</li>
 		</ul>
 		<h2>示例</h2>
 		<ul>
@@ -165,6 +166,13 @@
 			[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]
 		</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>
 
 	</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-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-style: normal;
 }
@@ -13,54 +20,86 @@
 }
 
 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-size: 16px;
-	line-height: 23px;
+	line-height: 24px;
 	tab-size: 4;
 	overflow: auto;
 }
 
 a {
-	color: #1184CE;
+	color: var(--color-blue);
 	cursor: pointer;
-	text-decoration: underline;
+	text-decoration: none;
 }
 
 h1 {
-	color: #049EF4;
-	font-size: 32px;
+	color: var(--color-blue);
+	font-size: 2.4em;
 	font-weight: normal;
-	line-height: 42px;
+	line-height: 1.36em;
+	margin-top: 16px;
+	margin-bottom: -16px;
+	text-indent: -2px;
 }
 
 h2 {
-	color: #4B0;
-
-	font-size: 22px;
+	color: var(--color-blue);
+	font-size: 1.8em;
+	line-height: 1.32em;
 	font-weight: normal;
-	line-height: 31px;
+	margin-top: 40px;
+	margin-bottom: 12px;
+	text-indent: -1px;
 }
 
 h3 {
-	color: #000;
-	font-size: 16px;
+	font-size: 1.32em;
+	line-height: 1.48em;
 	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;
-	max-width: 780px;
 }
 
 div {
-	/* padding-left: 30px; */
 	margin-bottom: 20px;
 }
 
@@ -68,17 +107,30 @@ div {
 	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 {
 	display: block;
-	padding: 20px;
+	padding: 20px 24px;
 	white-space: pre-wrap;
-	background-color: #f9f9f9;
 	overflow: auto;
+	box-sizing: border-box;
 }
 
 iframe {
@@ -109,15 +161,15 @@ strong {
 
 #button {
 	position: fixed;
-	bottom: 16px;
-	right: 16px;
+	bottom: 12px;
+	right: 12px;
 
 	padding: 8px;
 	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 {
@@ -127,6 +179,7 @@ strong {
 
 	#button img {
 		display: block;
+		width: 20px;
 	}
 
 a.permalink {
@@ -157,19 +210,17 @@ sub {
 /* mobile */
 
 @media all and ( max-width: 640px ) {
-
 	body {
-		margin: 14px auto;
-		padding: 0px 14px;
-		font-size: 14px;
-		line-height: 22px;
+		padding: 16px 20px;
 	}
 
 	h1 {
+		margin-top: 0;
 		font-size: 26px;
 	}
 
 	h2 {
+		margin-top: 20px;
 		font-size: 18px;
 		line-height: 25px;
 	}

+ 2 - 2
docs/prettify/threejs.css

@@ -11,7 +11,7 @@ pre .dec, code .dec { color: #22c0c4; } /* decimal */
 
 pre.prettyprint, code.prettyprint {
 	background-color: #F5F5F5;
-	font-family: 'RobotoMono', monospace;
+	font-family: 'Roboto Mono', monospace;
 	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",
+  "short_name": "Three.js",
+  "start_url": ".",
+  "scope": ".",
+  "display": "standalone",
   "icons": [
     {
       "src": "./images/icon.png",
       "type": "image/png",
       "sizes": "144x144"
     }
-  ],
-  "start_url": ".",
-  "display": "standalone"
+  ]
 }

+ 2 - 2
examples/css3d_panorama_deviceorientation.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
 	<head>
-		<title>three.js css3d - panorama - deviceorientation</title>
+		<title>three.js css3d - panorama - device orientation</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
@@ -34,7 +34,7 @@
 		<script src="js/controls/DeviceOrientationControls.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>
 

+ 1 - 0
examples/files.js

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

+ 66 - 24
examples/index.html

@@ -9,24 +9,29 @@
 		<style>
 			#panel #content .link {
 				display: block;
-				text-decoration: none;
-				cursor: pointer;
 			}
-
-			#viewSrcButton {
+			#button {
 				position: fixed;
-				bottom: 20px;
-				right: 20px;
+				bottom: 12px;
+				right: 12px;
+
 				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;
 				opacity: 1;
 			}
+
+			#button img {
+				display: block;
+				width: 20px;
+			}
 		</style>
 	</head>
 	<body>
@@ -34,25 +39,36 @@
 		<div id="panel">
 
 			<div id="header">
-
 				<h1><a href="http://threejs.org">three.js</a></h1>
 
-				<img id="expandButton" src="../files/ic_menu_black_24dp.svg">
-
 				<div id="sections">
-					<a href="../docs/">docs</a> <span class="selected">examples</span>
+					<a href="../docs/">docs</a>
+					<span class="selected">examples</span>
 				</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="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>
 
 		<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>
@@ -70,13 +86,22 @@
 		var viewer = document.getElementById( 'viewer' );
 
 		var filterInput = document.getElementById( 'filter' );
+		var exitSearchButton = document.getElementById( 'exitSearchButton' );
 
 		var expandButton = document.getElementById( 'expandButton' );
 		expandButton.addEventListener( 'click', function ( event ) {
 			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
 
 		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
@@ -90,12 +115,8 @@
 		var container = document.createElement( 'div' );
 		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';
-		document.body.appendChild( viewSrcButton );
 
 		var links = {};
 		var selected = null;
@@ -157,7 +178,7 @@
 			window.location.hash = file;
 			viewer.focus();
 
-			panel.classList.add( 'collapsed' );
+			panel.classList.remove( 'open' );
 
 			selected = file;
 
@@ -175,6 +196,27 @@
 		}
 
 		// 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 ) {
 

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

@@ -1740,7 +1740,12 @@ THREE.GLTFExporter.prototype = {
 
 			} 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 ] ) ) {
 
 					gltfNode.matrix = object.matrix.elements;

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

@@ -1,5 +1,17 @@
 /**
  * @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 ) {
@@ -63,7 +75,7 @@ THREE.ThreeMFLoader.prototype = {
 
 				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;
 
 				}
@@ -72,7 +84,7 @@ THREE.ThreeMFLoader.prototype = {
 
 			for ( file in zip.files ) {
 
-				if ( file.match( /\.rels$/ ) ) {
+				if ( file.match( /\_rels\/.rels$/ ) ) {
 
 					relsName = file;
 
@@ -110,7 +122,7 @@ THREE.ThreeMFLoader.prototype = {
 
 				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 ) {
+
+			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 = {};
 
@@ -300,6 +341,59 @@ THREE.ThreeMFLoader.prototype = {
 
 		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 ) {
@@ -383,7 +477,7 @@ THREE.ThreeMFLoader.prototype = {
 
 			if ( basematerialsNode ) {
 
-				resourcesData[ 'basematerial' ] = parseBasematerialsNode( basematerialsNode );
+				resourcesData[ 'basematerials' ] = parseBasematerialsNode( basematerialsNode );
 
 			}
 
@@ -411,25 +505,13 @@ THREE.ThreeMFLoader.prototype = {
 
 				var itemNode = itemNodes[ i ];
 				var buildItem = {
-					objectid: itemNode.getAttribute( 'objectid' )
+					objectId: itemNode.getAttribute( 'objectid' )
 				};
 				var transform = itemNode.getAttribute( '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();
 			geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
 			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();
 
-			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 );
 
 		}
 
-		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 ) {
 
@@ -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 meshes = {};
+			var objects = {};
 			var modelsKeys = Object.keys( modelsData );
 
 			for ( var i = 0; i < modelsKeys.length; i ++ ) {
 
 				var modelsKey = modelsKeys[ i ];
 				var modelData = modelsData[ modelsKey ];
-				var modelXml = modelData[ 'xml' ];
-				var extensions = modelData[ 'extensions' ];
 
 				var objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
 
 				for ( var j = 0; j < objectIds.length; 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 buildData = data3mf.model[ refs[ 'target' ].substring( 1 ) ][ 'build' ];
@@ -582,15 +893,19 @@ THREE.ThreeMFLoader.prototype = {
 			for ( var i = 0; i < buildData.length; 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 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 ++ ) {
 
-					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' ) {
 
-				object.userData = gltfDef.extras;
+				Object.assign( object.userData, gltfDef.extras );
 
 			} else {
 
@@ -2996,6 +2997,7 @@ THREE.GLTFLoader = ( function () {
 
 			if ( nodeDef.name !== undefined ) {
 
+				node.userData.name = 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
 		};
 
-		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';
 
+	} else {
+
+		this._pixelRatio = 1;
+		this._width = renderTarget.width;
+		this._height = renderTarget.height;
+
 	}
 
 	this.renderTarget1 = renderTarget;
@@ -162,10 +172,13 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 		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.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 ) {
 
-		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 ++ ) {
 
-			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 ) {
 
 		if ( _this.enabled === false ) return;
-		
+
 		event.preventDefault();
 
 		switch ( event.touches.length ) {

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

@@ -1764,7 +1764,12 @@ GLTFExporter.prototype = {
 
 			} 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 ] ) ) {
 
 					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 ++ ) {
 
-					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' ) {
 
-				object.userData = gltfDef.extras;
+				Object.assign( object.userData, gltfDef.extras );
 
 			} else {
 
@@ -3088,6 +3089,7 @@ var GLTFLoader = ( function () {
 
 			if ( nodeDef.name !== undefined ) {
 
+				node.userData.name = 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:
  *  var loader = new STLLoader();
  *  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:
  *  // use the same code to load STL as above
  *  if (geometry.hasColors) {
- *    material = new MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: VertexColors });
+ *    material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
  *  } else { .... }
- *  var mesh = new Mesh( geometry, material );
+ *  var mesh = new THREE.Mesh( geometry, material );
  */
 
 import {
@@ -36,10 +36,7 @@ import {
 	FileLoader,
 	Float32BufferAttribute,
 	LoaderUtils,
-	Mesh,
-	MeshPhongMaterial,
-	Vector3,
-	VertexColors
+	Vector3
 } 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
 	var shader = UnpackDepthRGBAShader;
 
-	var uniforms = new UniformsUtils.clone( shader.uniforms );
+	var uniforms = UniformsUtils.clone( shader.uniforms );
 	var material = new ShaderMaterial( {
 		uniforms: uniforms,
 		vertexShader: shader.vertexShader,
@@ -109,7 +109,7 @@ var ShadowMapViewer = function ( light ) {
 	}
 
 
-	function resetPosition () {
+	function resetPosition() {
 
 		scope.position.set( scope.position.x, scope.position.y );
 
@@ -187,6 +187,7 @@ var ShadowMapViewer = function ( light ) {
 			 camera.updateProjectionMatrix();
 
 			 this.update();
+
 		}
 
 	};

+ 2 - 3
examples/misc_controls_fly.html

@@ -252,12 +252,11 @@
 				SCREEN_HEIGHT = window.innerHeight;
 				SCREEN_WIDTH = window.innerWidth;
 
-				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
-
 				camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
 				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 mesh;
+			var line;
 
 			init();
 			animate();
@@ -99,8 +99,8 @@
 
 				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;
 
-				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 );
 

+ 5 - 5
examples/webgl_buffergeometry_lines_indexed.html

@@ -50,7 +50,7 @@
 
 			var camera, scene, renderer;
 
-			var mesh, parent_node;
+			var parent_node;
 
 			init();
 			animate();
@@ -201,12 +201,12 @@
 				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 				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.add( mesh );
+				parent_node.add( lineSegments );
 
 				scene.add( parent_node );
 

+ 6 - 6
examples/webgl_custom_attributes_lines.html

@@ -83,7 +83,7 @@
 
 		var renderer, scene, camera, stats;
 
-		var object, uniforms;
+		var line, uniforms;
 
 		var loader = new THREE.FontLoader();
 		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.setPixelRatio( window.devicePixelRatio );
@@ -197,12 +197,12 @@
 
 			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.color.value.offsetHSL( 0.0005, 0, 0 );
 
-			var attributes = object.geometry.attributes;
+			var attributes = line.geometry.attributes;
 			var array = attributes.displacement.array;
 
 			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>
 
 		<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>
 
 		<script src="../build/three.js"></script>
@@ -51,7 +52,7 @@
 			}
 
 			var params = {
-				exposure: 1.0
+				exposure: 2.0
 			};
 
 			var renderer, scene, camera;
@@ -76,7 +77,7 @@
 
 				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( texture );
@@ -92,7 +93,7 @@
 
 					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 );
 
@@ -106,7 +107,7 @@
 
 				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();
 
 				//

+ 6 - 5
examples/webgl_loader_texture_hdr.html

@@ -32,7 +32,8 @@
 	<body>
 
 		<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>
 
 		<script src="../build/three.js"></script>
@@ -51,7 +52,7 @@
 			}
 
 			var params = {
-				exposure: 1.0
+				exposure: 2.0
 			};
 
 			var renderer, scene, camera;
@@ -76,7 +77,7 @@
 
 				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( texture );
@@ -88,7 +89,7 @@
 
 					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 );
 
@@ -102,7 +103,7 @@
 
 				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();
 
 				//

+ 1 - 1
examples/webgl_materials_video.html

@@ -244,7 +244,7 @@
 				camera.updateProjectionMatrix();
 
 				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.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 );
 
@@ -156,17 +156,16 @@
 
 			function onWindowResize() {
 
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
 				camera.aspect = window.innerWidth / window.innerHeight;
 				camera.updateProjectionMatrix();
 
 				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();*/
 
 				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;
 
 			init();
@@ -45,7 +45,7 @@
 
 			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;
 
 				var scene1 = new THREE.Scene();
@@ -101,6 +101,21 @@
 				composer.addPass( clearMaskPass );
 				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() {

+ 1 - 5
examples/webgl_postprocessing_smaa.html

@@ -92,11 +92,7 @@
 				camera.updateProjectionMatrix();
 
 				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
 
 				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 );
 
 				var controls = new THREE.OrbitControls( camera, renderer.domElement );
@@ -145,8 +145,8 @@
 				renderer.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();
 
 				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
 
 				composer = new THREE.EffectComposer( renderer );
+				composer.setPixelRatio( 1 ); // ensure pixel ratio is always 1 for performance reasons
 				ssaaRenderPassP = new THREE.SSAARenderPass( scene, cameraP );
 				composer.addPass( ssaaRenderPassP );
 				ssaaRenderPassO = new THREE.SSAARenderPass( scene, cameraO );
@@ -204,11 +205,7 @@
 				cameraO.updateProjectionMatrix();
 
 				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 camera, scene, renderer;
-			var effectComposer;
-			var ssaoPass;
+			var composer;
 			var group;
 
 			init();
@@ -125,11 +124,11 @@
 				var width = window.innerWidth;
 				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
 				var gui = new dat.GUI();
@@ -152,8 +151,6 @@
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
-				onWindowResize();
-
 			}
 
 			function onWindowResize() {
@@ -163,9 +160,9 @@
 
 				camera.aspect = width / height;
 				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.y = timer * 0.0001;
 
-				effectComposer.render();
+				composer.render();
 
 			}
 

+ 1 - 5
examples/webgl_postprocessing_taa.html

@@ -172,11 +172,7 @@
 				camera.updateProjectionMatrix();
 
 				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;
 
 			composer = new THREE.EffectComposer( renderer );
-			composer.setSize( window.innerWidth, window.innerHeight );
 			composer.addPass( renderScene );
 			composer.addPass( bloomPass );
 

+ 2 - 4
examples/webgl_postprocessing_unreal_bloom_selective.html

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

+ 2 - 3
examples/webgl_shader_lava.html

@@ -199,12 +199,11 @@
 
 			function onWindowResize() {
 
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
 				camera.aspect = window.innerWidth / window.innerHeight;
 				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-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-style: normal;
 }
@@ -16,13 +24,11 @@ html, body {
 body {
 	margin: 0px;
 	overflow: hidden;
-
-	font-family: 'RobotoMono', monospace;
+	font-family: 'Roboto Mono', monospace;
 	font-size: 14px;
-	line-height: 23px;
-
+	line-height: 24px;
 	background-color: #ffffff;
-	color: #555;
+	color: var(--text-color);
 }
 
 a {
@@ -30,107 +36,146 @@ a {
 }
 
 h1 {
-	margin-top: 0px; /* reset */
-	margin-left: 15px;
-	margin-bottom: 20px;
-	padding-top: 13px;
-
 	font-size: 18px;
-	font-weight: normal;
-}
-
-h1 a {
-	color: #049EF4;
+	font-weight: 500;
 }
 
 h2 {
-	margin-top: 20px;
-
+	padding: 8px 0;
+	margin: 6px 0 12px 0;
 	font-size: 14px;
 	font-weight: normal;
-
-	color: #049EF4;
+	color: var(--color-blue);
 }
 
 h3 {
-	margin: 20px 0 0 0;
-
+	margin: 4px 0;
 	font-size: 14px;
 	line-height: 23px;
 	font-weight: 500;
 	text-transform: uppercase;
-
 	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 {
 	position: fixed;
 	left: 0px;
 	width: 300px;
 	height: 100%;
 	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 {
-		position: absolute;
-		right: 14px;
-		top: 14px;
+		margin-right: 14px;
+		margin-left: 4px;
 		display: none;
 	}
 
 	#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 * {
-			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 {
 			color: #9E9E9E;
 		}
 
 	#panel #filter {
 		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;
-		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 {
+		font-family: 'Roboto Mono', monospace;
 		position: absolute;
-		top: 98px;
-		right: 14px;
+		top: 9px;
+		right: 8px;
 		border: 0px;
 		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-color: white;
-		padding: 4px 26px 4px 8px;
+		padding: 4px 24px 4px 8px;
 		-webkit-appearance: none;
 		-moz-appearance: none;
 		appearance: none;
@@ -140,26 +185,39 @@ h3 {
 			outline: none;
 		}
 
+	#contentWrapper {
+		flex: 1;
+		overflow: hidden;
+		display: flex;
+		flex-direction: column;
+		transform: translate3d(0,0,0);
+	}
 	#panel #content {
-		margin: 0px 16px;
+		flex: 1;
+		overflow-y: scroll;
+		padding: 0 var(--panel-padding) 24px var(--panel-padding);
 	}
 
 		#panel #content ul {
 			list-style-type: none;
 			padding: 0px;
-			margin: 0px;
+			margin: 0px 0 24px 0;
+		}
+		#panel #content ul li {
+			margin: 2px 0;
 		}
 
 		#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 {
-			color: #ff0000;
+			text-decoration: underline;
 		}
 
 		#panel #content .hidden {
@@ -178,45 +236,67 @@ iframe {
 /* mobile */
 
 @media all and ( max-width: 640px ) {
-
+	#panel #expandButton {
+		display: block;
+	}
 	#panel {
 		position: absolute;
 		left: 0;
 		top: 0;
-		height: 100%;
 		width: 100%;
 		right: 0;
 		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;
 		left: 0;
-		top: 90px;
 		right: 0;
+		top: 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 {
 		position: absolute;
 		left: 0;
-		top: 92px;
+		top: var(--header-height);
 		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;
 
-				} else break handle_stop;
+				} else {
+
+					this.time = time;
+
+					break handle_stop;
+
+				}
 
 				if ( this.clampWhenFinished ) this.paused = true;
 				else this.enabled = false;
 
+				this.time = time;
+
 				this._mixer.dispatchEvent( {
 					type: 'finished', action: this,
 					direction: deltaTime < 0 ? - 1 : 1
@@ -559,6 +567,8 @@ Object.assign( AnimationAction.prototype, {
 
 					time = deltaTime > 0 ? duration : 0;
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 						type: 'finished', action: this,
 						direction: deltaTime > 0 ? 1 : - 1
@@ -583,26 +593,30 @@ Object.assign( AnimationAction.prototype, {
 
 					this._loopCount = loopCount;
 
+					this.time = time;
+
 					this._mixer.dispatchEvent( {
 						type: 'loop', action: this, loopDelta: loopDelta
 					} );
 
 				}
 
+			} else {
+
+				this.time = time;
+
 			}
 
 			if ( pingPong && ( loopCount & 1 ) === 1 ) {
 
 				// invert time for the "pong round"
 
-				this.time = time;
 				return duration - time;
 
 			}
 
 		}
 
-		this.time = 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 );
 
+	},
+
+	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 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;
 
@@ -1089,12 +1084,7 @@ BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototy
 
 				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;
 

+ 12 - 0
src/core/InstancedBufferAttribute.js

@@ -36,6 +36,18 @@ InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribu
 
 		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;
   bevelThickness?: number;
   bevelSize?: number;
+  bevelOffset?: number;
   bevelSegments?: number;
   extrudePath?: CurvePath<Vector3>;
   UVGenerator?: UVGenerator;

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

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

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

@@ -44,6 +44,7 @@ export interface MaterialParameters {
   dithering?: boolean;
   flatShading?: boolean;
   side?: Side;
+	shadowSide?: Side;
   transparent?: boolean;
   vertexColors?: Colors;
   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
 		 * @type {boolean}
 		 */
-		checkShaderErrors: false
+		checkShaderErrors: true
 	};
 
 	// 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;
 
+				if ( triggers[ i ] === undefined ) triggers[ i ] = false;
+
 				if ( 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 files = [
+	{ path: 'controls/DragControls.js', ignoreList: [] },
+	{ path: 'controls/DeviceOrientationControls.js', ignoreList: [] },
+	{ path: 'controls/EditorControls.js', ignoreList: [] },
 	{ path: 'controls/OrbitControls.js', ignoreList: [] },
 	{ path: 'controls/MapControls.js', ignoreList: [] },
+	{ path: 'controls/PointerLockControls.js', ignoreList: [] },
 	{ path: 'controls/TrackballControls.js', ignoreList: [] },
 	// { path: 'controls/TransformControls.js', ignoreList: [] },
 
@@ -20,10 +24,14 @@ var files = [
 	{ path: 'exporters/STLExporter.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/OBJLoader.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/PMREMGenerator.js', ignoreList: [] },

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