Mr.doob 7 years ago
parent
commit
94ad3e47a0
100 changed files with 5338 additions and 4242 deletions
  1. 733 530
      build/three.js
  2. 343 333
      build/three.min.js
  3. 732 529
      build/three.module.js
  4. 7 4
      docs/api/audio/Audio.html
  5. 10 7
      docs/api/audio/AudioAnalyser.html
  6. 11 8
      docs/api/audio/AudioListener.html
  7. 10 6
      docs/api/audio/PositionalAudio.html
  8. 1 1
      docs/api/cameras/OrthographicCamera.html
  9. 1 1
      docs/api/core/BufferGeometry.html
  10. 0 3
      docs/api/core/DirectGeometry.html
  11. 99 99
      docs/api/core/EventDispatcher.html
  12. 2 2
      docs/api/core/Geometry.html
  13. 20 5
      docs/api/core/InterleavedBuffer.html
  14. 20 35
      docs/api/core/InterleavedBufferAttribute.html
  15. 1 1
      docs/api/deprecated/DeprecatedList.html
  16. 31 0
      docs/api/extras/Earcut.html
  17. 2 36
      docs/api/extras/ShapeUtils.html
  18. 8 2
      docs/api/extras/core/Curve.html
  19. 11 11
      docs/api/extras/core/Path.html
  20. 5 0
      docs/api/extras/core/Shape.html
  21. 71 71
      docs/api/extras/curves/SplineCurve.html
  22. 2 2
      docs/api/geometries/ConeBufferGeometry.html
  23. 1 1
      docs/api/geometries/ConeGeometry.html
  24. 66 66
      docs/api/geometries/CylinderBufferGeometry.html
  25. 2 2
      docs/api/geometries/CylinderGeometry.html
  26. 1 1
      docs/api/geometries/ExtrudeBufferGeometry.html
  27. 2 3
      docs/api/geometries/TextBufferGeometry.html
  28. 104 104
      docs/api/geometries/TubeBufferGeometry.html
  29. 104 104
      docs/api/geometries/TubeGeometry.html
  30. 97 97
      docs/api/helpers/ArrowHelper.html
  31. 2 2
      docs/api/helpers/AxesHelper.html
  32. 1 1
      docs/api/lights/DirectionalLight.html
  33. 7 3
      docs/api/lights/shadows/SpotLightShadow.html
  34. 7 4
      docs/api/loaders/AnimationLoader.html
  35. 7 4
      docs/api/loaders/AudioLoader.html
  36. 8 5
      docs/api/loaders/BufferGeometryLoader.html
  37. 1 1
      docs/api/loaders/Cache.html
  38. 12 8
      docs/api/loaders/CubeTextureLoader.html
  39. 24 24
      docs/api/loaders/FileLoader.html
  40. 8 7
      docs/api/loaders/FontLoader.html
  41. 105 0
      docs/api/loaders/ImageBitmapLoader.html
  42. 12 25
      docs/api/loaders/ImageLoader.html
  43. 10 4
      docs/api/loaders/JSONLoader.html
  44. 0 8
      docs/api/loaders/Loader.html
  45. 38 0
      docs/api/loaders/LoaderUtils.html
  46. 8 5
      docs/api/loaders/MaterialLoader.html
  47. 22 22
      docs/api/loaders/ObjectLoader.html
  48. 11 8
      docs/api/loaders/TextureLoader.html
  49. 39 0
      docs/api/loaders/managers/LoadingManager.html
  50. 1 1
      docs/api/materials/MeshPhongMaterial.html
  51. 2 2
      docs/api/math/Box3.html
  52. 1 1
      docs/api/math/Color.html
  53. 1 1
      docs/api/math/Sphere.html
  54. 1 1
      docs/api/math/Vector3.html
  55. 1 1
      docs/api/math/Vector4.html
  56. 1 1
      docs/api/objects/Mesh.html
  57. 1 1
      docs/api/renderers/WebGLRenderTarget.html
  58. 5 2
      docs/api/renderers/WebGLRenderer.html
  59. 49 49
      docs/api/scenes/Fog.html
  60. 2 2
      docs/api/scenes/FogExp2.html
  61. 27 0
      docs/api/textures/DataTexture.html
  62. 1 1
      docs/api/textures/Texture.html
  63. 2 0
      docs/examples/exporters/GLTFExporter.html
  64. 19 9
      docs/examples/loaders/GLTFLoader.html
  65. 12 11
      docs/examples/loaders/LoaderSupport.html
  66. 1 1
      docs/examples/loaders/OBJLoader.html
  67. 1 1
      docs/examples/loaders/OBJLoader2.html
  68. 7 1
      docs/examples/renderers/CanvasRenderer.html
  69. 5 1
      docs/list.js
  70. 1 0
      docs/manual/introduction/Animation-system.html
  71. 123 0
      docs/manual/introduction/Browser-support.html
  72. 2 2
      docs/manual/introduction/Creating-a-scene.html
  73. 1 1
      docs/page.js
  74. 1 63
      editor/index.html
  75. 7 1
      editor/js/Config.js
  76. 164 0
      editor/js/Sidebar.Settings.Shortcuts.js
  77. 2 0
      editor/js/Sidebar.Settings.js
  78. 1 43
      editor/js/libs/tern-threejs/threejs.js
  79. 4 2
      examples/canvas_geometry_panorama.html
  80. 4 2
      examples/canvas_geometry_panorama_fisheye.html
  81. 9 6
      examples/files.js
  82. 3 3
      examples/index.html
  83. 1 1
      examples/js/QuickHull.js
  84. 260 38
      examples/js/exporters/GLTFExporter.js
  85. 0 654
      examples/js/libs/earcut.js
  86. 0 19
      examples/js/libs/pnltri.min.js
  87. 4 11
      examples/js/loaders/3MFLoader.js
  88. 1 9
      examples/js/loaders/AMFLoader.js
  89. 8 1
      examples/js/loaders/AssimpJSONLoader.js
  90. 1 1
      examples/js/loaders/AssimpLoader.js
  91. 3 13
      examples/js/loaders/BinaryLoader.js
  92. 46 5
      examples/js/loaders/ColladaLoader.js
  93. 348 0
      examples/js/loaders/EXRLoader.js
  94. 306 226
      examples/js/loaders/FBXLoader.js
  95. 226 0
      examples/js/loaders/GCodeLoader.js
  96. 470 356
      examples/js/loaders/GLTFLoader.js
  97. 0 147
      examples/js/loaders/ImageBitmapLoader.js
  98. 298 229
      examples/js/loaders/LoaderSupport.js
  99. 1 1
      examples/js/loaders/MMDLoader.js
  100. 84 126
      examples/js/loaders/OBJLoader2.js

File diff suppressed because it is too large
+ 733 - 530
build/three.js


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


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


+ 7 - 4
docs/api/audio/Audio.html

@@ -21,18 +21,21 @@
 
 		<h2>Example</h2>
 
-		<div>[example:misc_sound misc / sound ]</div>
+		<div>
+			[example:webaudio_sandbox webaudio / sandbox ]</br>
+			[example:webaudio_visualizer webaudio / visualizer ]
+		</div>
+
 		<code>
-		//Create an AudioListener and add it to the camera
+		// create an AudioListener and add it to the camera
 		var listener = new THREE.AudioListener();
 		camera.add( listener );
 
 		// create a global audio source
 		var sound = new THREE.Audio( listener );
 
+		// load a sound and set it as the Audio object's buffer
 		var audioLoader = new THREE.AudioLoader();
-
-		//Load a sound and set it as the Audio object's buffer
 		audioLoader.load( 'sounds/ambient.ogg', function( buffer ) {
 			sound.setBuffer( buffer );
 			sound.setLoop( true );

+ 10 - 7
docs/api/audio/AudioAnalyser.html

@@ -21,18 +21,21 @@
 
 		<h2>Example</h2>
 
-		<div>[example:misc_sound misc / sound ]</div>
+		<div>
+			[example:webaudio_sandbox webaudio / sandbox ]</br>
+			[example:webaudio_visualizer webaudio / visualizer ]
+		</div>
+
 		<code>
-		//Create an AudioListener and add it to the camera
+		// create an AudioListener and add it to the camera
 		var listener = new THREE.AudioListener();
 		camera.add( listener );
 
 		// create an Audio source
 		var sound = new THREE.Audio( listener );
 
+		// load a sound and set it as the Audio object's buffer
 		var audioLoader = new THREE.AudioLoader();
-
-		//Load a sound and set it as the Audio object's buffer
 		audioLoader.load( 'sounds/ambient.ogg', function( buffer ) {
 			sound.setBuffer( buffer );
 			sound.setLoop(true);
@@ -40,11 +43,11 @@
 			sound.play();
 		});
 
-		//Create an AudioAnalyser, passing in the sound and desired fftSize
+		// create an AudioAnalyser, passing in the sound and desired fftSize
 		var analyser = new THREE.AudioAnalyser( sound, 32 );
 
-		//Get the average frequency of the sound
-		analyser.getAverageFrequency();
+		// get the average frequency of the sound
+		var data = analyser.getAverageFrequency();
 		</code>
 
 

+ 11 - 8
docs/api/audio/AudioListener.html

@@ -13,27 +13,30 @@
 		<h1>[name]</h1>
 
 		<div class="desc">
-			Create a non-positional ( global ) audio object.<br /><br />
-
-			This uses the [link:https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API Web Audio API].
-
+			The [name] represents a virtual [link:https://developer.mozilla.org/de/docs/Web/API/AudioListener listener] of the all positional and non-positional audio effects in the scene.</br>
+			A three.js application usually creates a single instance of [name]. It is a mandatory construtor parameter for audios entities like [page:Audio Audio] and [page:PositionalAudio PositionalAudio].</br>
+			In most cases, the listener object is a child of the camera. So the 3D transformation of the camera represents the 3D transformation of the listener.
 		</div>
 
 
 		<h2>Example</h2>
 
-		<div>[example:misc_sound misc / sound ]</div>
+		<div>
+			[example:webaudio_sandbox webaudio / sandbox ]</br>
+			[example:webaudio_timing webaudio / timing ]</br>
+			[example:webaudio_visualizer webaudio / visualizer ]
+		</div>
+
 		<code>
-		//Create an AudioListener and add it to the camera
+		// create an AudioListener and add it to the camera
 		var listener = new THREE.AudioListener();
 		camera.add( listener );
 
 		// create a global audio source
 		var sound = new THREE.Audio( listener );
 
+		// load a sound and set it as the Audio object's buffer
 		var audioLoader = new THREE.AudioLoader();
-
-		//Load a sound and set it as the Audio object's buffer
 		audioLoader.load( 'sounds/ambient.ogg', function( buffer ) {
 			sound.setBuffer( buffer );
 			sound.setLoop(true);

+ 10 - 6
docs/api/audio/PositionalAudio.html

@@ -21,16 +21,20 @@
 
 		<h2>Example</h2>
 
-		<div>[example:misc_sound misc / sound ]</div>
+		<div>
+			[example:webaudio_sandbox webaudio / sandbox ]</br>
+			[example:webaudio_timing webaudio / timing ]
+		</div>
+
 		<code>
-		//Create an AudioListener and add it to the camera
+		// create an AudioListener and add it to the camera
 		var listener = new THREE.AudioListener();
 		camera.add( listener );
 
-		//Create the PositionalAudio object (passing in the listener)
+		// create the PositionalAudio object (passing in the listener)
 		var sound = new THREE.PositionalAudio( listener );
 
-		//Load a sound and set it as the PositionalAudio object's buffer
+		// load a sound and set it as the PositionalAudio object's buffer
 		var audioLoader = new THREE.AudioLoader();
 		audioLoader.load( 'sounds/song.ogg', function( buffer ) {
 			sound.setBuffer( buffer );
@@ -38,13 +42,13 @@
 			sound.play();
 		});
 
-		//Create an object for the sound to play from
+		// create an object for the sound to play from
 		var sphere = new THREE.SphereGeometry( 20, 32, 16 );
 		var material = new THREE.MeshPhongMaterial( { color: 0xff2200 } );
 		var mesh = new THREE.Mesh( sphere, material );
 		scene.add( mesh );
 
-		//Finally add the sound to the mesh
+		// finally add the sound to the mesh
 		mesh.add( sound );
 		</code>
 

+ 1 - 1
docs/api/cameras/OrthographicCamera.html

@@ -59,7 +59,7 @@ scene.add( camera );</code>
 		<h2>Properties</h2>
 		<div>
 			See the base [page:Camera] class for common properties.<br>
- 			Note that after making changes to most of these poperties you will have to call 
+ 			Note that after making changes to most of these properties you will have to call 
  			[page:OrthographicCamera.updateProjectionMatrix .updateProjectionMatrix] for the changes to take effect.
 		</div>
 

+ 1 - 1
docs/api/core/BufferGeometry.html

@@ -73,7 +73,7 @@
 
 		<h3>[page:BufferAttribute normal] (itemSize: 3)</h3>
 		<div>
-		Stores the x, y, and z components of the face or vertex normal vector of each vertex in this geometry.
+		Stores the x, y, and z components of the vertex normal vector of each vertex in this geometry.
 		Set by [page:.fromGeometry]().
 		</div>
 

+ 0 - 3
docs/api/core/DirectGeometry.html

@@ -82,9 +82,6 @@
 		<h3>[property:Boolean normalsNeedUpdate]</h3>
 		<div>Default is false.</div>
 
-		<h3>[property:Boolean verticesNeedUpdate]</h3>
-		<div>Default is false.</div>
-
 		<h3>[property:Boolean colorsNeedUpdate]</h3>
 		<div>Default is false.</div>
 

+ 99 - 99
docs/api/core/EventDispatcher.html

@@ -1,100 +1,100 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">
-			JavaScript events for custom objects.<br />
-			[link:https://github.com/mrdoob/eventdispatcher.js Eventdispatcher on GitHub]
-		</div>
-
-		<h2>Example</h2>
-
-		<code>
-// Adding events to a custom object
-
-var Car = function () {
-
-    this.start = function () {
-
-        this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
-
-    };
-
-};
-
-// Mixin the EventDispatcher.prototype with the custom object prototype
-
-Object.assign( Car.prototype, EventDispatcher.prototype );
-
-// Using events with the custom object
-
-var car = new Car();
-
-car.addEventListener( 'start', function ( event ) {
-
-    alert( event.message );
-
-} );
-
-car.start();
-		</code>
-
-		<h2>Constructor</h2>
-
-		<h3>[name]()</h3>
-		<div>
-		Creates EventDispatcher object.
-		</div>
-
-
-		<h2>Methods</h2>
-
-		<h3>[method:null addEventListener]( [page:String type], [page:Function listener] )</h3>
-		<div>
-		type - The type of event to listen to.<br />
-		listener - The function that gets called when the event is fired.
-		</div>
-		<div>
-		Adds a listener to an event type.
-		</div>
-
-		<h3>[method:Boolean hasEventListener]( [page:String type], [page:Function listener] )</h3>
-		<div>
-		type - The type of event to listen to.<br />
-		listener - The function that gets called when the event is fired.
-		</div>
-		<div>
-		Checks if listener is added to an event type.
-		</div>
-
-		<h3>[method:null removeEventListener]( [page:String type], [page:Function listener] )</h3>
-		<div>
-		type - The type of the listener that gets removed.<br />
-		listener - The listener function that gets removed.
-		</div>
-		<div>
-		Removes a listener from an event type.
-		</div>
-
-		<h3>[method:null dispatchEvent]( [page:object event] )</h3>
-		<div>
-		event - The event that gets fired.
-		</div>
-		<div>
-		Fire an event type.
-		</div>
-
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+			JavaScript events for custom objects.<br />
+			[link:https://github.com/mrdoob/eventdispatcher.js Eventdispatcher on GitHub]
+		</div>
+
+		<h2>Example</h2>
+
+		<code>
+// Adding events to a custom object
+
+var Car = function () {
+
+    this.start = function () {
+
+        this.dispatchEvent( { type: 'start', message: 'vroom vroom!' } );
+
+    };
+
+};
+
+// Mixing the EventDispatcher.prototype with the custom object prototype
+
+Object.assign( Car.prototype, EventDispatcher.prototype );
+
+// Using events with the custom object
+
+var car = new Car();
+
+car.addEventListener( 'start', function ( event ) {
+
+    alert( event.message );
+
+} );
+
+car.start();
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]()</h3>
+		<div>
+		Creates EventDispatcher object.
+		</div>
+
+
+		<h2>Methods</h2>
+
+		<h3>[method:null addEventListener]( [page:String type], [page:Function listener] )</h3>
+		<div>
+		type - The type of event to listen to.<br />
+		listener - The function that gets called when the event is fired.
+		</div>
+		<div>
+		Adds a listener to an event type.
+		</div>
+
+		<h3>[method:Boolean hasEventListener]( [page:String type], [page:Function listener] )</h3>
+		<div>
+		type - The type of event to listen to.<br />
+		listener - The function that gets called when the event is fired.
+		</div>
+		<div>
+		Checks if listener is added to an event type.
+		</div>
+
+		<h3>[method:null removeEventListener]( [page:String type], [page:Function listener] )</h3>
+		<div>
+		type - The type of the listener that gets removed.<br />
+		listener - The listener function that gets removed.
+		</div>
+		<div>
+		Removes a listener from an event type.
+		</div>
+
+		<h3>[method:null dispatchEvent]( [page:object event] )</h3>
+		<div>
+		event - The event that gets fired.
+		</div>
+		<div>
+		Fire an event type.
+		</div>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 2
docs/api/core/Geometry.html

@@ -159,8 +159,8 @@
 		In code another example could look like this:
 		<code>
 		// e.g.
-		geometry.skinIndices[15] = new THREE.Vector4(   0,   5,   9, 0 );
-		geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3, 0 );
+		geometry.skinIndices[15] = new THREE.Vector4(   0,   5,   9, 10 );
+		geometry.skinWeights[15] = new THREE.Vector4( 0.2, 0.5, 0.3,  0 );
 
 		// corresponds with the following vertex
 		geometry.vertices[15];

+ 20 - 5
docs/api/core/InterleavedBuffer.html

@@ -11,21 +11,32 @@
 		<h1>[name]</h1>
 
 		<div class="desc">
+			"Interleaved" means that multiple attributes, possibly of different types, (e.g., position, normal, uv, color) are packed into a single array buffer.
+			<br/><br/>
+			An introduction into interleaved arrays can be found here: [link:https://blog.tojicode.com/2011/05/interleaved-array-basics.html Interleaved array basics]
 		</div>
 
+		<h2>Example</h2>
+
+		<div>[example:webgl_buffergeometry_points_interleaved webgl / buffergeometry / points / interleaved]</div>
+
 		<h2>Constructor</h2>
 		<h3>[name]( [page:TypedArray array], [page:Integer stride] )</h3>
 		<div>
+			[page:TypedArray array] -- A typed array with a shared buffer. Stores the geometry data.<br/>
+			[page:Integer stride] -- The number of typed-array elements per vertex.
 		</div>
 
 		<h2>Properties</h2>
 
 		<h3>[property:Array array]</h3>
 		<div>
+			 A typed array with a shared buffer. Stores the geometry data.
 		</div>
 
 		<h3>[property:Integer stride]</h3>
 		<div>
+			The number of typed-array elements per vertex.
 		</div>
 
 		<h3>[property:Integer count]</h3>
@@ -45,7 +56,7 @@
 
 		<h3>[property:Number updateRange.offset]</h3>
 		<div>
-		DEfault is *0*.
+		Default is *0*.
 		</div>
 
 		<h3>[property:Number updateRange.count]</h3>
@@ -82,19 +93,23 @@
 
 		<h3>[method:InterleavedBuffer copy]( source ) </h3>
 		<div>
-		 Copy the array, count, stride and value of dynamic to this.
+		 Copies another [name] to this [name].
 		</div>
 
 		<h3>[method:InterleavedBuffer copyAt]( index1, attribute, index2 ) </h3>
-		<div>
-		</div>
+		<div>Copies data from attribute[index2] to [page:InterleavedBuffer.array array][index1].</div>
 
 		<h3>[method:InterleavedBuffer set]( value, offset ) </h3>
 		<div>
+			value - The source (typed) array.<br/>
+			offset - The offset into the target array at which to begin writing values from the source array. Default is *0*.<br/><br />
+
+			Stores multiple values in the buffer, reading input values from a specified array.
 		</div>
 
-		<h3>[method:InterleavedBuffer clone]( index, x, y ) </h3>
+		<h3>[method:InterleavedBuffer clone]() </h3>
 		<div>
+			Creates a clone of this [name].
 		</div>
 
 		<h2>Source</h2>

+ 20 - 35
docs/api/core/InterleavedBufferAttribute.html

@@ -36,16 +36,18 @@
 		<div>
 			The value of [page:InterleavedBufferAttribute.data data].count.
 
-			If the buffer is storing a 3-component vector (such as a position, normal, or color),
-			then this will count the number of such vectors stored.
+			If the buffer is storing a 3-component item (such as a position, normal, or color),
+			then this will count the number of such items stored.
 		</div>
 
 		<h3>[property:Integer itemSize]</h3>
 		<div>
+			How many values make up each item.
 		</div>
 
 		<h3>[property:Integer offset]</h3>
 		<div>
+			The offset in the underlying array buffer where an item starts.
 		</div>
 
 		<h3>[property:Boolean normalized]</h3>
@@ -60,55 +62,38 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null getX]( index ) </h3>
-		<div>
-
-		</div>
-
-		<h3>[method:null getY]( index ) </h3>
-		<div>
+		<h3>[method:Number getX]( index ) </h3>
+		<div>Returns the x component of the item at the given index.</div>
 
-		</div>
+		<h3>[method:Number getY]( index ) </h3>
+		<div>Returns the y component of the item at the given index.</div>
 
-		<h3>[method:null getZ]( index ) </h3>
-		<div>
+		<h3>[method:Number getZ]( index ) </h3>
+		<div>Returns the z component of the item at the given index.</div>
 
-		</div>
-
-		<h3>[method:null getW]( index ) </h3>
-		<div>
-
-		</div>
+		<h3>[method:Number getW]( index ) </h3>
+		<div>Returns the w component of the item at the given index.</div>
 
 		<h3>[method:null setX]( index, x ) </h3>
-		<div>
-
-		</div>
+		<div>Sets the x component of the item at the given index.</div>
 
 		<h3>[method:null setY]( index, y ) </h3>
-		<div>
-
-		</div>
+		<div>Sets the y component of the item at the given index.</div>
 
 		<h3>[method:null setZ]( index, z ) </h3>
-		<div>
+		<div>Sets the z component of the item at the given index.</div>
 
-		</div>
+		<h3>[method:null setW]( index, w ) </h3>
+		<div>Sets the w component of the item at the given index.</div>
 
 		<h3>[method:null setXY]( index, x, y ) </h3>
-		<div>
-
-		</div>
+		<div>Sets the x and y components of the item at the given index.</div>
 
 		<h3>[method:null setXYZ]( index, x, y, z ) </h3>
-		<div>
-
-		</div>
+		<div>Sets the x, y and z components of the item at the given index.</div>
 
 		<h3>[method:null setXYZW]( index, x, y, z, w ) </h3>
-		<div>
-
-		</div>
+		<div>Sets the x, y, z and w components of the item at the given index.</div>
 
 
 

+ 1 - 1
docs/api/deprecated/DeprecatedList.html

@@ -458,7 +458,7 @@
 
 
 		<h3>[page:Particle]</h3>
-		<div>ParticleSystem has been renamed to [page:Sprite].</div>
+		<div>Particle has been renamed to [page:Sprite].</div>
 
 		<h3>[page:ParticleSystem]</h3>
 		<div>ParticleSystem has been renamed to [page:Points].</div>

+ 31 - 0
docs/api/extras/Earcut.html

@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+		An implementation of the earcut polygon triangulation algorithm. The code is a port of [link:https://github.com/mapbox/earcut mapbox/earcut].
+		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:Array triangulate]( data, holeIndices, dim )</h3>
+		<div>
+		data -- A flat array of vertice coordinates.<br /><br />
+		holeIndices -- An array of hole indices if any.<br /><br />
+		dim -- The number of coordinates per vertice in the input array.<br /><br />
+
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 36
docs/api/extras/ShapeUtils.html

@@ -28,30 +28,6 @@
 
 		</div>
 
-		<h3>[method:Number b2]( t, p0, p1, p2 )</h3>
-		<div>
-		t -- number<br />
-		p0, p1, p2 -- x, y, z or w components of a quadratic bezier curve.<br /><br />
-
-		Note that this is a linear function so it is neccessary to calculate separately for
-		x, y (and z for 3D curves) components of a curve.<br /><br />
-
-		Used internally by [page:QuadraticBezierCurve QuadraticBezierCurve],
-		[page:QuadraticBezierCurve3 QuadraticBezierCurve3] and [page:Font Font].
-		</div>
-
-		<h3>[method:Number b3]( t, p0, p1, p2, p3 )</h3>
-		<div>
-		t -- number. <br />
-		p0, p1, p2, p3 -- x, y or z components of a cubic bezier curve..<br /><br />
-
-		Note that this is a linear function so it is neccessary to calculate separately for
-		x, y (and z for 3D curves) components of a curve.<br /><br />
-
-		Used internally by [page:CubicBezierCurve CubicBezierCurve],
-		[page:CubicBezierCurve3 CubicBezierCurve3] and [page:Font Font].
-		</div>
-
 		<h3>[method:Boolean isClockwise]( pts )</h3>
 		<div>
 		pts -- points defining a 2D polygon<br /><br />
@@ -60,16 +36,7 @@
 		x, y  components of a polygon.<br /><br />
 
 		Used internally by [page:Path Path],
-		[page:ExtrudeGeometry ExtrudeGeometry] and [page:ShapeBufferGeometry ShapeBufferGeometry].
-		</div>
-
-		<h3>[method:null triangulate]( contour, indices )</h3>
-		<div>
-		contour --  2D polygon.<br />
-		indices -- <br /><br />
-
-		Used internally by [page:ExtrudeGeometry ExtrudeGeometry]
-		and [page:ShapeBufferGeometry ShapeBufferGeometry] to calculate faces.
+		[page:ExtrudeGeometry ExtrudeGeometry] and [page:ShapeGeometry ShapeGeometry].
 		</div>
 
 		<h3>[method:null triangulateShape]( contour, holes )</h3>
@@ -77,8 +44,7 @@
 		contour -- 2D polygon.<br />
 		holes -- array of holes<br /><br />
 
-		Used internally by [page:ExtrudeGeometry ExtrudeGeometry]
-		and [page:ShapeBufferGeometry ShapeBufferGeometry] to calculate faces in shapes with holes.
+		Used internally by [page:ExtrudeGeometry ExtrudeGeometry] and [page:ShapeGeometry ShapeGeometry] to calculate faces in shapes with holes.
 		</div>
 
 		<h2>Source</h2>

+ 8 - 2
docs/api/extras/core/Curve.html

@@ -98,10 +98,16 @@
 		</div>
 
 		<h3>[method:Curve clone]()</h3>
-		<div>Creates a clone of this curve.</div>
+		<div>Creates a clone of this instance.</div>
 
 		<h3>[method:Curve copy]( [page:Curve source] )</h3>
-		<div>Copies another curve to this instance.</div>
+		<div>Copies another [name] object to this instance.</div>
+
+		<h3>[method:Object toJSON]()</h3>
+		<div>Returns a JSON object representation of this instance.</div>
+
+		<h3>[method:Curve fromJSON]( [page:Object json] )</h3>
+		<div>Copies the data from the given JSON object to this instance.</div>
 
 		<h2>Source</h2>
 

+ 11 - 11
docs/api/extras/core/Path.html

@@ -20,9 +20,9 @@
 		<h2>Example</h2>
 
 		<code>
-var v1 = new THREE.Vector3();
-var v2 = new THREE.Vector3(1, 45, 6);
-var v3 = new THREE.Vector3(34, 34, 676);
+var v1 = new THREE.Vector2();
+var v2 = new THREE.Vector2(1, 45);
+var v3 = new THREE.Vector2(34, 34);
 
 var vectors = [v1, v2, v3];
 
@@ -94,14 +94,6 @@ var path = new THREE.Path(vectors);
 		<h3>[method:null bezierCurveTo]( [page:Float cp1X], [page:Float cp1Y], [page:Float cp2X], [page:Float cp2Y], [page:Float x], [page:Float y] )</h3>
 		<div>This creates a bezier curve from [page:.currentPoint] with (cp1X, cp1Y) and (cp2X, cp2Y) as control points and updates [page:.currentPoint] to x and y.</div>
 
-		<h3>[method:null fromPoints]( [page:Array vector2s] )</h3>
-		<div>
-			points --  array of [page:Vector2 Vector2s].<br /><br />
-
-			Adds to the from the points. Points	are added to the [page:CurvePath.curves curves]
-			array as [page:LineCurve LineCurves].
-		</div>
-
 		<h3>[method:null ellipse]( [page:Float x], [page:Float y], [page:Float xRadius], [page:Float yRadius], [page:Float startAngle], [page:Float endAngle], [page:Float clockwise], [page:Float rotation] )</h3>
 		<div>
 			x, y -- The center of the ellipse offset from the last call.<br />
@@ -126,6 +118,14 @@ var path = new THREE.Path(vectors);
 		<h3>[method:null quadraticCurveTo]( [page:Float cpX], [page:Float cpY], [page:Float x], [page:Float y] )</h3>
 		<div>Creates a quadratic curve from [page:.currentPoint] with cpX and cpY as control point and updates [page:.currentPoint] to x and y.</div>
 
+		<h3>[method:null setFromPoints]( [page:Array vector2s] )</h3>
+		<div>
+			points --  array of [page:Vector2 Vector2s].<br /><br />
+
+			Points are added to the [page:CurvePath.curves curves]
+			array as [page:LineCurve LineCurves].
+		</div>
+
 		<h3>[method:null splineThru] ( [page:Array points] ) </h3>
 		<div>
 			points - An array of [page:Vector2 Vector2s]<br /><br />

+ 5 - 0
docs/api/extras/core/Shape.html

@@ -62,6 +62,11 @@
 		<h2>Properties</h2>
 		<div>See the base [page:Path] class for common properties.</div>
 
+		<h3>[property:String uuid]</h3>
+		<div>
+		[link:http://en.wikipedia.org/wiki/Universally_unique_identifier UUID] of this instance. This gets automatically assigned, so this shouldn't be edited.
+		</div>
+
 		<h3>[property:array holes]</h3>
 		<div>An array of [page:Path paths] that define the holes in the shape.</div>
 

+ 71 - 71
docs/api/extras/curves/SplineCurve.html

@@ -1,72 +1,72 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Curve] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">
-		Create a smooth 2d spline curve from a series of points. Internally this uses
-		[page:Interpolations.CatmullRom] to create the curve.
-		</div>
-
-		<h2>Example</h2>
-
-<code>
-// Create a sine-like wave
-var curve = new THREE.SplineCurve( [
-	new THREE.Vector2( -10, 0 ),
-	new THREE.Vector2( -5, 5 ),
-	new THREE.Vector2( 0, 0 ),
-	new THREE.Vector2( 5, -5 ),
-	new THREE.Vector2( 10, 0 )
-] );
-
-var points = curve.getPoints( 50 );
-var geometry = new THREE.BufferGeometry().setFromPoints( points );
-
-var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
-
-// Create the final object to add to the scene
-var splineObject = new THREE.Line( geometry, material );
-</code>
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]( [page:Array points] )</h3>
-		<div>points – An array of [page:Vector2] points that define the curve.</div>
-
-
-		<h2>Properties</h2>
-		<div>See the base [page:Curve] class for common properties.</div>
-
-		<h3>[property:Boolean isSplineCurve]</h3>
-		<div>
-			Used to check whether this or derived classes are SplineCurves. Default is *true*.<br /><br />
-
-			You should not change this, as it used internally for optimisation.
-		</div>
-
-		<h3>[property:Array points]</h3>
-		<div>The array of [page:Vector3] points that define the curve.</div>
-
-
-
-		<h2>Methods</h2>
-		<div>See the base [page:Curve] class for common methods.</div>
-
-
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Curve] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">
+		Create a smooth 2d spline curve from a series of points. Internally this uses
+		[page:Interpolations.CatmullRom] to create the curve.
+		</div>
+
+		<h2>Example</h2>
+
+<code>
+// Create a sine-like wave
+var curve = new THREE.SplineCurve( [
+	new THREE.Vector2( -10, 0 ),
+	new THREE.Vector2( -5, 5 ),
+	new THREE.Vector2( 0, 0 ),
+	new THREE.Vector2( 5, -5 ),
+	new THREE.Vector2( 10, 0 )
+] );
+
+var points = curve.getPoints( 50 );
+var geometry = new THREE.BufferGeometry().setFromPoints( points );
+
+var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
+
+// Create the final object to add to the scene
+var splineObject = new THREE.Line( geometry, material );
+</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:Array points] )</h3>
+		<div>points – An array of [page:Vector2] points that define the curve.</div>
+
+
+		<h2>Properties</h2>
+		<div>See the base [page:Curve] class for common properties.</div>
+
+		<h3>[property:Boolean isSplineCurve]</h3>
+		<div>
+			Used to check whether this or derived classes are SplineCurves. Default is *true*.<br /><br />
+
+			You should not change this, as it used internally for optimisation.
+		</div>
+
+		<h3>[property:Array points]</h3>
+		<div>The array of [page:Vector2] points that define the curve.</div>
+
+
+
+		<h2>Methods</h2>
+		<div>See the base [page:Curve] class for common methods.</div>
+
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 2
docs/api/geometries/ConeBufferGeometry.html

@@ -42,11 +42,11 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]([page:Float radius], [page:Float height], [page:Integer radiusSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
+		<h3>[name]([page:Float radius], [page:Float height], [page:Integer radialSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
 		<div>
 		radius — Radius of the cone base. Default is 20.<br />
 		height — Height of the cone. Default is 100.<br />
-		radiusSegments — Number of segmented faces around the circumference of the cone. Default is 8<br />
+		radialSegments — Number of segmented faces around the circumference of the cone. Default is 8<br />
 		heightSegments — Number of rows of faces along the height of the cone. Default is 1.<br />
 		openEnded — A Boolean indicating whether the base of the cone is open or capped. Default is false, meaning capped.<br />
 		thetaStart — Start angle for first segment, default = 0 (three o'clock position).<br />

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

@@ -46,7 +46,7 @@
 		<div>
 		radius — Radius of the cone at the base. Default is 20.<br />
 		height — Height of the cone. Default is 100.<br />
-		radiusSegments — Number of segmented faces around the circumference of the cone. Default is 8<br />
+		radialSegments — Number of segmented faces around the circumference of the cone. Default is 8<br />
 		heightSegments — Number of rows of faces along the height of the cone. Default is 1.<br />
 		openEnded — A Boolean indicating whether the base of the cone is open or capped. Default is false, meaning capped.<br />
 		thetaStart — Start angle for first segment, default = 0 (three o'clock position).<br />

+ 66 - 66
docs/api/geometries/CylinderBufferGeometry.html

@@ -1,67 +1,67 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:BufferGeometry] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">This is the [page:BufferGeometry] port of [page:CylinderGeometry].</div>
-
-		<iframe id="scene" src="scenes/geometry-browser.html#CylinderBufferGeometry"></iframe>
-
-		<script>
-
-		// iOS iframe auto-resize workaround
-
-		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
-
-			var scene = document.getElementById( 'scene' );
-
-			scene.style.width = getComputedStyle( scene ).width;
-			scene.style.height = getComputedStyle( scene ).height;
-			scene.setAttribute( 'scrolling', 'no' );
-
-		}
-
-		</script>
-
-		<h2>Example</h2>
-
-		<code>var geometry = new THREE.CylinderBufferGeometry( 5, 5, 20, 32 );
-		var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
-		var cylinder = new THREE.Mesh( geometry, material );
-		scene.add( cylinder );
-		</code>
-
-		<h2>Constructor</h2>
-
-		<h3>[name]([page:Float radiusTop], [page:Float radiusBottom], [page:Float height], [page:Integer radiusSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
-		<div>
-		radiusTop — Radius of the cylinder at the top. Default is 20.<br />
-		radiusBottom — Radius of the cylinder at the bottom. Default is 20.<br />
-		height — Height of the cylinder. Default is 100.<br />
-		radiusSegments — Number of segmented faces around the circumference of the cylinder. Default is 8<br />
-		heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.<br />
-		openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.<br />
-		thetaStart — Start angle for first segment, default = 0 (three o'clock position).<br />
-		thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
-		</div>
-
-		<h2>Properties</h2>
-
-		<div>
-		Each of the constructor parameters is accessible as a property of the same name. Any modification of these properties after instantiation does not change the geometry.
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/geometries/CylinderGeometry.js src/geometries/CylinderGeometry.js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:BufferGeometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">This is the [page:BufferGeometry] port of [page:CylinderGeometry].</div>
+
+		<iframe id="scene" src="scenes/geometry-browser.html#CylinderBufferGeometry"></iframe>
+
+		<script>
+
+		// iOS iframe auto-resize workaround
+
+		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
+
+			var scene = document.getElementById( 'scene' );
+
+			scene.style.width = getComputedStyle( scene ).width;
+			scene.style.height = getComputedStyle( scene ).height;
+			scene.setAttribute( 'scrolling', 'no' );
+
+		}
+
+		</script>
+
+		<h2>Example</h2>
+
+		<code>var geometry = new THREE.CylinderBufferGeometry( 5, 5, 20, 32 );
+		var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
+		var cylinder = new THREE.Mesh( geometry, material );
+		scene.add( cylinder );
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]([page:Float radiusTop], [page:Float radiusBottom], [page:Float height], [page:Integer radialSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
+		<div>
+		radiusTop — Radius of the cylinder at the top. Default is 20.<br />
+		radiusBottom — Radius of the cylinder at the bottom. Default is 20.<br />
+		height — Height of the cylinder. Default is 100.<br />
+		radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8<br />
+		heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.<br />
+		openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.<br />
+		thetaStart — Start angle for first segment, default = 0 (three o'clock position).<br />
+		thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
+		</div>
+
+		<h2>Properties</h2>
+
+		<div>
+		Each of the constructor parameters is accessible as a property of the same name. Any modification of these properties after instantiation does not change the geometry.
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/geometries/CylinderGeometry.js src/geometries/CylinderGeometry.js]
+	</body>
+</html>

+ 2 - 2
docs/api/geometries/CylinderGeometry.html

@@ -42,12 +42,12 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]([page:Float radiusTop], [page:Float radiusBottom], [page:Float height], [page:Integer radiusSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
+		<h3>[name]([page:Float radiusTop], [page:Float radiusBottom], [page:Float height], [page:Integer radialSegments], [page:Integer heightSegments], [page:Boolean openEnded], [page:Float thetaStart], [page:Float thetaLength])</h3>
 		<div>
 		radiusTop — Radius of the cylinder at the top. Default is 1.<br />
 		radiusBottom — Radius of the cylinder at the bottom. Default is 1.<br />
 		height — Height of the cylinder. Default is 1.<br />
-		radiusSegments — Number of segmented faces around the circumference of the cylinder. Default is 8<br />
+		radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8<br />
 		heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.<br />
 		openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.<br />
 		thetaStart — Start angle for first segment, default = 0 (three o'clock position).<br />

+ 1 - 1
docs/api/geometries/ExtrudeBufferGeometry.html

@@ -140,6 +140,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		[link:https://github.com/mrdoob/three.js/blob/master/src/ExtrudeGeometry.js src/ExtrudeGeometry.js]
 	</body>
 </html>

+ 2 - 3
docs/api/geometries/TextBufferGeometry.html

@@ -39,8 +39,7 @@
 		<h2>Examples</h2>
 
 		<div>
-		[example:webgl_geometry_text geometry / text ]<br/>
-		[example:webgl_geometry_text_pnltri geometry / text2 ]
+		[example:webgl_geometry_text geometry / text ]
 		</div>
 
 		<code>
@@ -156,6 +155,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		[link:https://github.com/mrdoob/three.js/blob/master/src/TextGeometry.js src/TextGeometry.js]
 	</body>
 </html>

+ 104 - 104
docs/api/geometries/TubeBufferGeometry.html

@@ -1,105 +1,105 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:BufferGeometry] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">Creates a tube that extrudes along a 3d curve.</div>
-
-		<iframe id="scene" src="scenes/geometry-browser.html#TubeBufferGeometry"></iframe>
-
-		<script>
-
-		// iOS iframe auto-resize workaround
-
-		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
-
-			var scene = document.getElementById( 'scene' );
-
-			scene.style.width = getComputedStyle( scene ).width;
-			scene.style.height = getComputedStyle( scene ).height;
-			scene.setAttribute( 'scrolling', 'no' );
-
-		}
-
-		</script>
-
-		<h2>Example</h2>
-
-		<code>
-		function CustomSinCurve( scale ) {
-
-			THREE.Curve.call( this );
-
-			this.scale = ( scale === undefined ) ? 1 : scale;
-
-		}
-
-		CustomSinCurve.prototype = Object.create( THREE.Curve.prototype );
-		CustomSinCurve.prototype.constructor = CustomSinCurve;
-
-		CustomSinCurve.prototype.getPoint = function ( t ) {
-
-			var tx = t * 3 - 1.5;
-			var ty = Math.sin( 2 * Math.PI * t );
-			var tz = 0;
-
-			return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );
-
-		};
-
-		var path = new CustomSinCurve( 10 );
-		var geometry = new THREE.TubeBufferGeometry( path, 20, 2, 8, false );
-		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
-		var mesh = new THREE.Mesh( geometry, material );
-		scene.add( mesh );
-		</code>
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:Curve path], [page:Integer tubularSegments], [page:Float radius], [page:Integer radiusSegments], [page:Boolean closed])</h3>
-		<div>
-		path — [page:Curve] - A path that inherits from the [page:Curve] base class<br />
-		tubularSegments — [page:Integer] - The number of segments that make up the tube, default is 64<br />
-		radius — [page:Float] - The radius of the tube, default is 1<br />
-		radiusSegments — [page:Integer] - The number of segments that make up the cross-section, default is 8 <br />
-		closed — [page:Boolean] Is the tube open or closed, default is false <br />
-		</div>
-
-
-		<h2>Properties</h2>
-
-		<h3>[property:Object parameters]</h3>
-		<div>
-		An object with all of the parameters that were used to generate the geometry.
-		</div>
-
-		<h3>[property:Array tangents]</h3>
-		<div>
-		An array of [page:Vector3] tangents
-		</div>
-
-		<h3>[property:Array normals]</h3>
-		<div>
-		An array of [page:Vector3] normals
-		</div>
-
-		<h3>[property:Array binormals]</h3>
-		<div>
-		An array of [page:Vector3] binormals
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js src/geometries/TubeGeometry.js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:BufferGeometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">Creates a tube that extrudes along a 3d curve.</div>
+
+		<iframe id="scene" src="scenes/geometry-browser.html#TubeBufferGeometry"></iframe>
+
+		<script>
+
+		// iOS iframe auto-resize workaround
+
+		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
+
+			var scene = document.getElementById( 'scene' );
+
+			scene.style.width = getComputedStyle( scene ).width;
+			scene.style.height = getComputedStyle( scene ).height;
+			scene.setAttribute( 'scrolling', 'no' );
+
+		}
+
+		</script>
+
+		<h2>Example</h2>
+
+		<code>
+		function CustomSinCurve( scale ) {
+
+			THREE.Curve.call( this );
+
+			this.scale = ( scale === undefined ) ? 1 : scale;
+
+		}
+
+		CustomSinCurve.prototype = Object.create( THREE.Curve.prototype );
+		CustomSinCurve.prototype.constructor = CustomSinCurve;
+
+		CustomSinCurve.prototype.getPoint = function ( t ) {
+
+			var tx = t * 3 - 1.5;
+			var ty = Math.sin( 2 * Math.PI * t );
+			var tz = 0;
+
+			return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );
+
+		};
+
+		var path = new CustomSinCurve( 10 );
+		var geometry = new THREE.TubeBufferGeometry( path, 20, 2, 8, false );
+		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+		var mesh = new THREE.Mesh( geometry, material );
+		scene.add( mesh );
+		</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]([page:Curve path], [page:Integer tubularSegments], [page:Float radius], [page:Integer radialSegments], [page:Boolean closed])</h3>
+		<div>
+		path — [page:Curve] - A path that inherits from the [page:Curve] base class<br />
+		tubularSegments — [page:Integer] - The number of segments that make up the tube, default is 64<br />
+		radius — [page:Float] - The radius of the tube, default is 1<br />
+		radialSegments — [page:Integer] - The number of segments that make up the cross-section, default is 8 <br />
+		closed — [page:Boolean] Is the tube open or closed, default is false <br />
+		</div>
+
+
+		<h2>Properties</h2>
+
+		<h3>[property:Object parameters]</h3>
+		<div>
+		An object with all of the parameters that were used to generate the geometry.
+		</div>
+
+		<h3>[property:Array tangents]</h3>
+		<div>
+		An array of [page:Vector3] tangents
+		</div>
+
+		<h3>[property:Array normals]</h3>
+		<div>
+		An array of [page:Vector3] normals
+		</div>
+
+		<h3>[property:Array binormals]</h3>
+		<div>
+		An array of [page:Vector3] binormals
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js src/geometries/TubeGeometry.js]
+	</body>
+</html>

+ 104 - 104
docs/api/geometries/TubeGeometry.html

@@ -1,105 +1,105 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Geometry] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">Creates a tube that extrudes along a 3d curve.</div>
-
-		<iframe id="scene" src="scenes/geometry-browser.html#TubeGeometry"></iframe>
-
-		<script>
-
-		// iOS iframe auto-resize workaround
-
-		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
-
-			var scene = document.getElementById( 'scene' );
-
-			scene.style.width = getComputedStyle( scene ).width;
-			scene.style.height = getComputedStyle( scene ).height;
-			scene.setAttribute( 'scrolling', 'no' );
-
-		}
-
-		</script>
-
-		<h2>Example</h2>
-
-		<code>
-		function CustomSinCurve( scale ) {
-
-			THREE.Curve.call( this );
-
-			this.scale = ( scale === undefined ) ? 1 : scale;
-
-		}
-
-		CustomSinCurve.prototype = Object.create( THREE.Curve.prototype );
-		CustomSinCurve.prototype.constructor = CustomSinCurve;
-
-		CustomSinCurve.prototype.getPoint = function ( t ) {
-
-			var tx = t * 3 - 1.5;
-			var ty = Math.sin( 2 * Math.PI * t );
-			var tz = 0;
-
-			return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );
-
-		};
-
-		var path = new CustomSinCurve( 10 );
-		var geometry = new THREE.TubeGeometry( path, 20, 2, 8, false );
-		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
-		var mesh = new THREE.Mesh( geometry, material );
-		scene.add( mesh );
-		</code>
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:Curve path], [page:Integer tubularSegments], [page:Float radius], [page:Integer radiusSegments], [page:Boolean closed])</h3>
-		<div>
-		path — [page:Curve] - A path that inherits from the [page:Curve] base class<br />
-		tubularSegments — [page:Integer] - The number of segments that make up the tube, default is 64<br />
-		radius — [page:Float] - The radius of the tube, default is 1<br />
-		radiusSegments — [page:Integer] - The number of segments that make up the cross-section, default is 8 <br />
-		closed — [page:Boolean] Is the tube open or closed, default is false <br />
-		</div>
-
-
-		<h2>Properties</h2>
-
-		<h3>[property:Object parameters]</h3>
-		<div>
-		An object with all of the parameters that were used to generate the geometry.
-		</div>
-
-		<h3>[property:Array tangents]</h3>
-		<div>
-		An array of [page:Vector3] tangents
-		</div>
-
-		<h3>[property:Array normals]</h3>
-		<div>
-		An array of [page:Vector3] normals
-		</div>
-
-		<h3>[property:Array binormals]</h3>
-		<div>
-		An array of [page:Vector3] binormals
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Geometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">Creates a tube that extrudes along a 3d curve.</div>
+
+		<iframe id="scene" src="scenes/geometry-browser.html#TubeGeometry"></iframe>
+
+		<script>
+
+		// iOS iframe auto-resize workaround
+
+		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
+
+			var scene = document.getElementById( 'scene' );
+
+			scene.style.width = getComputedStyle( scene ).width;
+			scene.style.height = getComputedStyle( scene ).height;
+			scene.setAttribute( 'scrolling', 'no' );
+
+		}
+
+		</script>
+
+		<h2>Example</h2>
+
+		<code>
+		function CustomSinCurve( scale ) {
+
+			THREE.Curve.call( this );
+
+			this.scale = ( scale === undefined ) ? 1 : scale;
+
+		}
+
+		CustomSinCurve.prototype = Object.create( THREE.Curve.prototype );
+		CustomSinCurve.prototype.constructor = CustomSinCurve;
+
+		CustomSinCurve.prototype.getPoint = function ( t ) {
+
+			var tx = t * 3 - 1.5;
+			var ty = Math.sin( 2 * Math.PI * t );
+			var tz = 0;
+
+			return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );
+
+		};
+
+		var path = new CustomSinCurve( 10 );
+		var geometry = new THREE.TubeGeometry( path, 20, 2, 8, false );
+		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+		var mesh = new THREE.Mesh( geometry, material );
+		scene.add( mesh );
+		</code>
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]([page:Curve path], [page:Integer tubularSegments], [page:Float radius], [page:Integer radialSegments], [page:Boolean closed])</h3>
+		<div>
+		path — [page:Curve] - A path that inherits from the [page:Curve] base class<br />
+		tubularSegments — [page:Integer] - The number of segments that make up the tube, default is 64<br />
+		radius — [page:Float] - The radius of the tube, default is 1<br />
+		radialSegments — [page:Integer] - The number of segments that make up the cross-section, default is 8 <br />
+		closed — [page:Boolean] Is the tube open or closed, default is false <br />
+		</div>
+
+
+		<h2>Properties</h2>
+
+		<h3>[property:Object parameters]</h3>
+		<div>
+		An object with all of the parameters that were used to generate the geometry.
+		</div>
+
+		<h3>[property:Array tangents]</h3>
+		<div>
+		An array of [page:Vector3] tangents
+		</div>
+
+		<h3>[property:Array normals]</h3>
+		<div>
+		An array of [page:Vector3] normals
+		</div>
+
+		<h3>[property:Array binormals]</h3>
+		<div>
+		An array of [page:Vector3] binormals
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 97 - 97
docs/api/helpers/ArrowHelper.html

@@ -1,98 +1,98 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Object3D] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">An 3D arrow object for visualizing directions.</div>
-
-
-		<h2>Example</h2>
-
-		<div>[example:webgl_geometries WebGL / geometries]</div>
-		<div>[example:webgl_geometry_normals WebGL / geometry / normals]</div>
-		<div>[example:webgl_shadowmesh WebGL / shadowmesh]</div>
-
-		<code>
-		var dir = new THREE.Vector3( 1, 2, 0 );
-
-		//normalize the direction vector (convert to vector of length 1)
-		dir.normalize();
-
-		var origin = new THREE.Vector3( 0, 0, 0 );
-		var length = 1;
-		var hex = 0xffff00;
-
-		var arrowHelper = new THREE.ArrowHelper( dir, origin, length, hex );
-		scene.add( arrowHelper );
-		</code>
-
-
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:Vector3 dir], [page:Vector3 origin], [page:Number length], [page:Number hex], [page:Number headLength], [page:Number headWidth] )</h3>
-		<div>
-		[page:Vector3 dir] -- direction from origin. Must be a unit vector. <br />
-		[page:Vector3 origin] -- Point at which the arrow starts.<br />
-		[page:Number length] -- length of the arrow. Default is *1*.<br />
-		[page:Number hex] -- hexadecimal value to define color. Default is 0xffff00.<br />
-		[page:Number headLength] -- The length of the head of the arrow. Default is 0.2 * length.<br />
-		[page:Number headWidth] -- The length of the width of the arrow. Default is 0.2 * headLength.
-		</div>
-
-		<h2>Properties</h2>
-		<div>See the base [page:Object3D] class for common properties.</div>
-
-
-		<h3>[property:Line line]</h3>
-		<div>Contains the line part of the arrowHelper.</div>
-
-		<h3>[property:Mesh cone]</h3>
-		<div>Contains the cone part of the arrowHelper.</div>
-
-
-
-
-		<h2>Methods</h2>
-		<div>See the base [page:Object3D] class for common methods.</div>
-
-
-
-		<h3>[method:null setColor]([page:Number hex])</h3>
-		<div>
-		hex -- The hexadicmal value of the color.<br /><br />
-
-		Sets the color of the arrowHelper.
-		</div>
-
-		<h3>[method:null setLength]([page:Number length], [page:Number headLength], [page:Number headWidth])</h3>
-		<div>
-		length -- The desired length.<br />
-		headLength -- The length of the head of the arrow.<br />
-		headWidth -- The length of the width of the arrow.<br /><br />
-
-		Sets the length of the arrowhelper.
-		</div>
-
-		<h3>[method:null setDirection]([page:Vector3 dir])</h3>
-		<div>
-		dir -- The desired direction. Must be a unit vector.<br /><br />
-
-		Sets the direction of the arrowhelper.
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Object3D] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">An 3D arrow object for visualizing directions.</div>
+
+
+		<h2>Example</h2>
+
+		<div>[example:webgl_geometries WebGL / geometries]</div>
+		<div>[example:webgl_geometry_normals WebGL / geometry / normals]</div>
+		<div>[example:webgl_shadowmesh WebGL / shadowmesh]</div>
+
+		<code>
+		var dir = new THREE.Vector3( 1, 2, 0 );
+
+		//normalize the direction vector (convert to vector of length 1)
+		dir.normalize();
+
+		var origin = new THREE.Vector3( 0, 0, 0 );
+		var length = 1;
+		var hex = 0xffff00;
+
+		var arrowHelper = new THREE.ArrowHelper( dir, origin, length, hex );
+		scene.add( arrowHelper );
+		</code>
+
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]([page:Vector3 dir], [page:Vector3 origin], [page:Number length], [page:Number hex], [page:Number headLength], [page:Number headWidth] )</h3>
+		<div>
+		[page:Vector3 dir] -- direction from origin. Must be a unit vector. <br />
+		[page:Vector3 origin] -- Point at which the arrow starts.<br />
+		[page:Number length] -- length of the arrow. Default is *1*.<br />
+		[page:Number hex] -- hexadecimal value to define color. Default is 0xffff00.<br />
+		[page:Number headLength] -- The length of the head of the arrow. Default is 0.2 * length.<br />
+		[page:Number headWidth] -- The length of the width of the arrow. Default is 0.2 * headLength.
+		</div>
+
+		<h2>Properties</h2>
+		<div>See the base [page:Object3D] class for common properties.</div>
+
+
+		<h3>[property:Line line]</h3>
+		<div>Contains the line part of the arrowHelper.</div>
+
+		<h3>[property:Mesh cone]</h3>
+		<div>Contains the cone part of the arrowHelper.</div>
+
+
+
+
+		<h2>Methods</h2>
+		<div>See the base [page:Object3D] class for common methods.</div>
+
+
+
+		<h3>[method:null setColor]([page:Number hex])</h3>
+		<div>
+		hex -- The hexadecimal value of the color.<br /><br />
+
+		Sets the color of the arrowHelper.
+		</div>
+
+		<h3>[method:null setLength]([page:Number length], [page:Number headLength], [page:Number headWidth])</h3>
+		<div>
+		length -- The desired length.<br />
+		headLength -- The length of the head of the arrow.<br />
+		headWidth -- The length of the width of the arrow.<br /><br />
+
+		Sets the length of the arrowhelper.
+		</div>
+
+		<h3>[method:null setDirection]([page:Vector3 dir])</h3>
+		<div>
+		dir -- The desired direction. Must be a unit vector.<br /><br />
+
+		Sets the direction of the arrowhelper.
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 2
docs/api/helpers/AxesHelper.html

@@ -12,7 +12,7 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">An axis object to visualize the the 3 axes in a simple way. <br />
+		<div class="desc">An axis object to visualize the 3 axes in a simple way. <br />
 			The X axis is red. The Y axis is green. The Z axis is blue.</div>
 
 
@@ -35,7 +35,7 @@ scene.add( axesHelper );
 
 		<h3>[name]( [page:Number size] )</h3>
 		<div>
-		[page:Number size] -- (optional )size of the lines representing the axes. Default is *1*.
+		[page:Number size] -- (optional) size of the lines representing the axes. Default is *1*.
 		</div>
 
 		<h2>Properties</h2>

+ 1 - 1
docs/api/lights/DirectionalLight.html

@@ -60,7 +60,7 @@ scene.add( directionalLight );
 
 		<h2>Constructor</h2>
 
-		<h3>[name]( [page:Integer hex], [page:Float intensity] )</h3>
+		<h3>[name]( [page:Integer color], [page:Float intensity] )</h3>
 		<div>
 			[page:Integer color] - (optional) hexadecimal color of the light. Default is 0xffffff (white).<br />
 			[page:Float intensity] - (optional) numeric value of the light's strength/intensity. Default is 1.<br /><br />

+ 7 - 3
docs/api/lights/shadows/SpotLightShadow.html

@@ -70,9 +70,13 @@ scene.add( helper );
 		 The light's view of the world. This is used to generate a depth map of the scene; objects behind
 		 other objects from the light's perspective will be in shadow.<br /><br />
 
-		 The default is a  [page:PerspectiveCamera] with [page:PerspectiveCamera.fov fov] of 90,
-	  [page:PerspectiveCamera.aspect aspect] of 1, [page:PerspectiveCamera.near near]
-		clipping plane at 0.5 and	[page:PerspectiveCamera.far far] clipping plane at 500.
+		 The default is a  [page:PerspectiveCamera] with [page:PerspectiveCamera.near near] clipping plane at 0.5.
+		 The [page:PerspectiveCamera.fov fov] will track the [page:SpotLight.angle angle] property of the owning
+		 [page:SpotLight SpotLight] via the [page:SpotLightShadow.update update] method. Similarly, the 
+		 [page:PerspectiveCamera.aspect aspect] property will track the aspect of the
+		 [page:LightShadow.mapSize mapSize]. If the [page:SpotLight.distance distance] property of the light is 
+		 set, the [page:PerspectiveCamera.far far] clipping plane will track that, otherwise it defaults to 500.
+		 
 	 </div>
 
 	 <h3>[property:Boolean isSpotLightShadow]</h3>

+ 7 - 4
docs/api/loaders/AnimationLoader.html

@@ -25,16 +25,19 @@
 		loader.load(
 			// resource URL
 			'animations/animation.js',
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( animations ) {
 				// animations is an array of AnimationClips
 			},
-			// Function called when download progresses
+
+			// onProgress callback
 			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
-			// Function called when download errors
-			function ( xhr ) {
+
+			// onError callback
+			function ( err ) {
 				console.log( 'An error happened' );
 			}
 		);

+ 7 - 4
docs/api/loaders/AudioLoader.html

@@ -38,7 +38,8 @@
 		loader.load(
 			// resource URL
 			'audio/ambient_ocean.ogg',
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( audioBuffer ) {
 				// set the audio object buffer to the loaded object
 				oceanAmbientSound.setBuffer( audioBuffer );
@@ -46,12 +47,14 @@
 				// play the audio
 				oceanAmbientSound.play();
 			},
-			// Function called when download progresses
+
+			// onProgress callback
 			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
-			// Function called when download errors
-			function ( xhr ) {
+
+			// onError callback
+			function ( err ) {
 				console.log( 'An error happened' );
 			}
 		);

+ 8 - 5
docs/api/loaders/BufferGeometryLoader.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<meta charset="utf-8" />
+		<meta charset="utf-8" />
 		<base href="../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
@@ -27,18 +27,21 @@
 		loader.load(
 			// resource URL
 			'models/json/pressure.json',
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( geometry ) {
 				var material = new THREE.MeshLambertMaterial( { color: 0xF5F5F5 } );
 				var object = new THREE.Mesh( geometry, material );
 				scene.add( object );
 			},
-			// Function called when download progresses
+
+			// onProgress callback
 			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
-			// Function called when download errors
-			function ( xhr ) {
+
+			// onError callback
+			function ( err ) {
 				console.log( 'An error happened' );
 			}
 		);

+ 1 - 1
docs/api/loaders/Cache.html

@@ -17,7 +17,7 @@
 		<h2>Examples</h2>
 
 		<div>
-			[example:webgl_geometry_text_earcut WebGL / geometry / text / earcut]<br />
+			[example:webgl_geometry_text WebGL / geometry / text ]<br />
 			[example:webgl_interactive_instances_gpu WebGL / interactive / instances / gpu]<br />
 			[example:webgl_loader_ttf WebGL / loader / ttf]
 		</div>

+ 12 - 8
docs/api/loaders/CubeTextureLoader.html

@@ -31,12 +31,12 @@ var scene = new THREE.Scene();
 scene.background = new THREE.CubeTextureLoader()
 	.setPath( 'textures/cubeMaps/' )
 	.load( [
-				'1.png',
-				'2.png',
-				'3.png',
-				'4.png',
-				'5.png',
-				'6.png'
+				'px.png',
+				'nx.png',
+				'py.png',
+				'ny.png',
+				'pz.png',
+				'nz.png'
 			] );
 		</code>
 
@@ -73,8 +73,12 @@ scene.background = new THREE.CubeTextureLoader()
 
 		<h3>[method:null load]( [page:String urls], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
 		<div>
-		[page:String urls] — array of 6 urls to images, one for each side of the CubeTexture. These can also be
-			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URIs].<br />
+		[page:String urls] — array of 6 urls to images, one for each side of the CubeTexture.
+		The urls should be specified in the following order: pos-x, neg-x, pos-y, neg-y, pos-z, neg-z.
+		They can also be [link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URIs].<br />
+		Note that, by convention, cube maps are specified in a coordinate system in which positive-x is to the right
+		when looking up the positive-z axis -- in other words, using a left-handed coordinate system.
+		Since three.js uses a right-handed coordinate system, environment maps used in three.js will have pos-x and neg-x swapped.<br />
 		[page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Texture texture].<br />
 		[page:Function onProgress] — Will be called while load progresses. The argument will be the XMLHttpRequest instance, which contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
 		[page:Function onError] — Will be called when load errors.<br />

+ 24 - 24
docs/api/loaders/FileLoader.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<meta charset="utf-8" />
+		<meta charset="utf-8" />
 		<base href="../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
@@ -22,29 +22,29 @@
 			[example:webgl_morphtargets_human WebGL / morphtargets / human]<br />
 		</div>
 		<code>
-var loader = new THREE.FileLoader();
-
-//load a text file a output the result to the console
-loader.load(
-    // resource URL
-    'example.txt',
-
-    // Function when resource is loaded
-    function ( data ) {
-        // output the text to the console
-        console.log( data )
-    },
-
-    // Function called when download progresses
-    function ( xhr ) {
-        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
-    },
-
-    // Function called when download errors
-    function ( xhr ) {
-        console.error( 'An error happened' );
-    }
-);
+		var loader = new THREE.FileLoader();
+
+		//load a text file a output the result to the console
+		loader.load(
+			// resource URL
+			'example.txt',
+
+			// onLoad callback
+			function ( data ) {
+				// output the text to the console
+				console.log( data )
+			},
+
+			// onProgress callback
+			function ( xhr ) {
+				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+			},
+
+			// onError callback
+			function ( err ) {
+				console.error( 'An error happened' );
+			}
+		);
 		</code>
 
 		<h2>Constructor</h2>

+ 8 - 7
docs/api/loaders/FontLoader.html

@@ -22,9 +22,7 @@
 
 		<div>
 		[example:webgl_geometry_text_shapes geometry / text / shapes ]<br/>
-		[example:webgl_geometry_text geometry / text ]<br />
-		[example:webgl_geometry_text_earcut geometry / text / earcut]<br />
-		[example:webgl_geometry_text_pnltri geometry / text / pnltri]<br />
+		[example:webgl_geometry_text geometry / text ]
 		</div>
 
 		<code>
@@ -32,17 +30,20 @@
 		var font = loader.load(
 			// resource URL
 			'fonts/helvetiker_bold.typeface.json'
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( font ) {
 				// do something with the font
 				scene.add( font );
 			},
-			// Function called when download progresses
+
+			// onProgress callback
 			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
-			// Function called when download errors
-			function ( xhr ) {
+
+			// onError callback
+			function ( err ) {
 				console.log( 'An error happened' );
 			}
 		);

+ 105 - 0
docs/api/loaders/ImageBitmapLoader.html

@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+			A loader for loading an [page:Image] as an [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap]. An ImageBitmap provides an asynchronous and resource efficient pathway to prepare textures for rendering in WebGL.
+
+		</div>
+
+		<h2>Example</h2>
+
+		<div>
+			[example:webgl_loader_imagebitmap WebGL / loader / ImageBitmap]
+		</div>
+
+		<code>
+		// instantiate a loader
+		var loader = new THREE.ImageBitmapLoader();
+
+		// load a image resource
+		loader.load(
+			// resource URL
+			'textures/skyboxsun25degtest.png',
+
+			// onLoad callback
+			function ( imageBitmap ) {
+				var texture = new THREE.CanvasTexture( imageBitmap );
+				var material = new THREE.MeshBasicMaterial( { map: texture } );
+			},
+
+			// onProgress callback currently not supported
+			undefined,
+
+			// onError callback
+			function ( err ) {
+				console.log( 'An error happened' );
+			}
+		);
+		</code>
+
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [page:LoadingManager manager] )</h3>
+		<div>
+		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].<br /><br />
+
+		Creates a new [name].
+		</div>
+
+		<h2>Properties</h2>
+
+		<h3>[property:LoadingManager manager]</h3>
+		<div>
+			The [page:LoadingManager loadingManager] the loader is using. Default is [page:DefaultLoadingManager].
+		</div>
+
+		<h3>[property:String options]</h3>
+		<div>An optional object that sets options for the internally used [link:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap createImageBitmap] factory method. Default is *undefined*.</div>
+
+		<h3>[property:String path]</h3>
+		<div>The base path from which files will be loaded. See [page:.setPath]. Default is *undefined*.</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
+		<div>
+		[page:String url] — the path or URL to the file. This can also be a
+			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI].<br />
+		[page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Image image].<br />
+		[page:Function onProgress] — Will be called while load progresses. The argument will be the progress event.<br />
+		[page:Function onError] — Will be called when load errors.<br />
+		</div>
+		<div>
+		Begin loading from url and return the [page:ImageBitmap image] object that will contain the data.
+		</div>
+
+		<h3>[method:ImageBitmapLoader setCrossOrigin]()</h3>
+		<div>This method exists for compatibility reasons and implements no logic. It ensures that [name] has a similar interface like [page:ImageLoader].</div>
+
+		<h3>[method:ImageBitmapLoader setOptions]( [page:Object options] )</h3>
+		<div>
+			Sets the options object for [link:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap createImageBitmap].
+		</div>
+
+		<h3>[method:ImageBitmapLoader setPath]( [page:String path] )</h3>
+		<div>
+			Sets the base path or URL from which to load files. This can be useful if
+			you are loading many images from the same directory.
+		</div>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 12 - 25
docs/api/loaders/ImageLoader.html

@@ -32,26 +32,26 @@
 		loader.load(
 			// resource URL
 			'textures/skyboxsun25degtest.png',
-			// Function when resource is loaded
-			function ( image ) {
-				// do something with it
 
-				// like drawing a part of it on a canvas
+			// onLoad callback
+			function ( image ) {
+				// use the image, e.g. draw part of it on a canvas
 				var canvas = document.createElement( 'canvas' );
 				var context = canvas.getContext( '2d' );
 				context.drawImage( image, 100, 100 );
 			},
-			// Function called when download progresses
-			function ( xhr ) {
-				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
-			},
-			// Function called when download errors
-			function ( xhr ) {
-				console.log( 'An error happened' );
+
+			// onProgress callback currently not supported
+			undefined,
+
+			// onError callback
+			function () {
+				console.error( 'An error happened.' );
 			}
 		);
 		</code>
 
+		Please note three.js r84 dropped support for ImageLoader progress events. For an ImageLoader that supports progress events, see [link:https://github.com/mrdoob/three.js/issues/10439#issuecomment-275785639 this thread].
 
 		<h2>Constructor</h2>
 
@@ -78,12 +78,6 @@
 		<h3>[property:String path]</h3>
 		<div>The base path from which files will be loaded. See [page:.setPath]. Default is *undefined*.</div>
 
-		<h3>[property:String withCredentials]</h3>
-		<div>
-			Whether the XMLHttpRequest uses credentials - see [page:.setWithCredentials].
-			Default is *undefined*.
-		</div>
-
 		<h2>Methods</h2>
 
 		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
@@ -104,16 +98,9 @@
 		<h3>[method:FileLoader setPath]( [page:String path] )</h3>
 		<div>
 			Set the base path or URL from which to load files. This can be useful if
-			you are loading many models from the same directory.
+			you are loading many images from the same directory.
 		</div>
 
-		<h3>[method:FileLoader setWithCredentials]( [page:Boolean value] )</h3>
-		Whether the XMLHttpRequest uses credentials such as cookies, authorization headers or
-		TLS client certificates. See
-		[link:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials XMLHttpRequest.withCredentials].<br />
-		Note that this has no effect if you are loading files locally or from the same domain.
-		<div>
-
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

+ 10 - 4
docs/api/loaders/JSONLoader.html

@@ -28,18 +28,24 @@
 
 		// load a resource
 		loader.load(
-
 			// resource URL
 			'models/animated/monster/monster.js',
 
-			// Function when resource is loaded
+			// onLoad callback
 			function ( geometry, materials ) {
-
 				var material = materials[ 0 ];
 				var object = new THREE.Mesh( geometry, material );
-
 				scene.add( object );
+			},
+
+			// onProgress callback
+			function ( xhr ) {
+				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+			},
 
+			// onError callback
+			function( err ) {
+				console.log( 'An error happened' );
 			}
 		);
 		</code>

+ 0 - 8
docs/api/loaders/Loader.html

@@ -61,14 +61,6 @@
 		Creates an array of [page:Material] based on the array of parameters m. The index of the parameters decide the correct index of the materials.
 		</div>
 
-		<h3>[method:String extractUrlBase]( [page:string url] )</h3>
-		<div>
-		[page:String url] —  The url to extract the base url from.
-		</div>
-		<div>
-		Extract the base from the URL.
-		</div>
-
 
 		<h2>Source</h2>
 

+ 38 - 0
docs/api/loaders/LoaderUtils.html

@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">An object with several loader utility functions.</div>
+
+		<h2>Functions</h2>
+
+		<h3>[method:String decodeText]( [page:TypedArray array] )</h3>
+		<div>
+		[page:TypedArray array] —  A stream of bytes as a typed array.
+		</div>
+		<div>
+		The function takes a stream of bytes as input and returns a string representation.
+		</div>
+
+		<h3>[method:String extractUrlBase]( [page:string url] )</h3>
+		<div>
+		[page:String url] —  The url to extract the base url from.
+		</div>
+		<div>
+		Extract the base from the URL.
+		</div>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 8 - 5
docs/api/loaders/MaterialLoader.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<meta charset="utf-8" />
+		<meta charset="utf-8" />
 		<base href="../../" />
 		<script src="list.js"></script>
 		<script src="page.js"></script>
@@ -26,16 +26,19 @@
 		loader.load(
 			// resource URL
 			'path/to/material.json',
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( material ) {
 				object.material = material;
 			},
-			// Function called when download progresses
+
+			// onProgress callback
 			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
-			// Function called when download errors
-			function ( xhr ) {
+
+			// onError callback
+			function ( err ) {
 				console.log( 'An error happened' );
 			}
 		);

+ 22 - 22
docs/api/loaders/ObjectLoader.html

@@ -29,35 +29,35 @@
 		</div>
 
 		<code>
-var loader = new THREE.ObjectLoader();
+		var loader = new THREE.ObjectLoader();
 
-loader.load(
-    // resource URL
-    "models/json/example.json",
+		loader.load(
+			// resource URL
+			"models/json/example.json",
 
-    // pass the loaded data to the onLoad function.
-		//Here it is assumed to be an object
-    function ( obj ) {
-				//add the loaded object to the scene
-        scene.add( obj );
-    },
+			// onLoad callback
+			// Here the loaded data is assumed to be an object
+			function ( obj ) {
+				// Add the loaded object to the scene
+				scene.add( obj );
+			},
 
-    // Function called when download progresses
-    function ( xhr ) {
-        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
-    },
+			// onProgress callback
+			function ( err ) {
+				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+			},
 
-    // Function called when download errors
-    function ( xhr ) {
-        console.error( 'An error happened' );
-    }
-);
+			// onError callback
+			function ( xhr ) {
+				console.error( 'An error happened' );
+			}
+		);
 
 
-// Alternatively, to parse a previously loaded JSON structure
-var object = loader.parse( a_json_object );
+		// Alternatively, to parse a previously loaded JSON structure
+		var object = loader.parse( a_json_object );
 
-scene.add( object );
+		scene.add( object );
 		</code>
 
 

+ 11 - 8
docs/api/loaders/TextureLoader.html

@@ -36,24 +36,27 @@
 		loader.load(
 			// resource URL
 			'textures/land_ocean_ice_cloud_2048.jpg',
-			// Function when resource is loaded
+
+			// onLoad callback
 			function ( texture ) {
 				// in this example we create the material when the texture is loaded
 				var material = new THREE.MeshBasicMaterial( {
 					map: texture
 				 } );
 			},
-			// Function called when download progresses
-			function ( xhr ) {
-				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
-			},
-			// Function called when download errors
-			function ( xhr ) {
-				console.error( 'An error happened' );
+
+			// onProgress callback currently not supported
+			undefined,
+
+			// onError callback
+			function ( err ) {
+				console.error( 'An error happened.' );
 			}
 		);
 		</code>
 
+		Please note three.js r84 dropped support for TextureLoader progress events. For a TextureLoader that supports progress events, see [link:https://github.com/mrdoob/three.js/issues/10439#issuecomment-293260145 this thread].
+
 		<h2>Constructor</h2>
 
 		<h3>[name]( [page:LoadingManager manager] )</h3>

+ 39 - 0
docs/api/loaders/managers/LoadingManager.html

@@ -31,6 +31,10 @@
 			[example:webgl_terrain_dynamic WebGL / terrain / dynamic]
 		</div>
 
+		<p>
+			This example shows how to use LoadingManager to track the progress of
+			[page:OBJLoader].
+		</p>
 
 		<code>
 		var manager = new THREE.LoadingManager();
@@ -67,6 +71,41 @@
 		} );
 		</code>
 
+		<p>
+			In addition to observing progress, a LoadingManager can be used to
+			override resource URLs during loading. This may be helpful for assets
+			coming from drag-and-drop events, WebSockets, WebRTC, or other APIs. An
+			example showing how to load an in-memory model using Blob URLs is below.
+		</p>
+
+		<code>
+		// Blob or File objects created when dragging files into the webpage.
+		var blobs = {'fish.gltf': blob1, 'diffuse.png': blob2, 'normal.png': blob3};
+
+		var manager = new THREE.LoadingManager();
+
+		// Initialize loading manager with URL callback.
+		var objectURLs = [];
+		manager.setURLModifier( ( url ) => {
+
+			url = URL.createObjectURL( blobs[ url ] );
+
+			objectURLs.push( url );
+
+			return url;
+
+		} );
+
+		// Load as usual, then revoke the blob URLs.
+		var loader = new THREE.GLTFLoader( manager );
+		loader.load( 'fish.gltf', (gltf) => {
+
+			scene.add( gltf.scene );
+
+			objectURLs.forEach( ( url ) => URL.revokeObjectURL( url ) );
+
+		});
+		</code>
 
 		<h2>Constructor</h2>
 

+ 1 - 1
docs/api/materials/MeshPhongMaterial.html

@@ -205,7 +205,7 @@
 		<h3>[property:Boolean skinning]</h3>
 		<div>Define whether the material uses skinning. Default is false.</div>
 
-		<h3>[property:Texture specular]</h3>
+		<h3>[property:Color specular]</h3>
 		<div>
 			Specular color of the material. Default is a [page:Color] set to *0x111111* (very dark grey).<br /><br />
 

+ 2 - 2
docs/api/math/Box3.html

@@ -47,7 +47,7 @@
 
 		<h3>[property:Vector3 max]</h3>
 		<div>
-			[page:Vector3] representing the lower upper (x, y, z) boundary of the box.<br />
+			[page:Vector3] representing the upper (x, y, z) boundary of the box.<br />
 			Default is ( - Infinity, - Infinity, - Infinity ).
 		</div>
 
@@ -280,7 +280,7 @@
 		[page:Vector3 offset] - Direction and distance of offset.<br /><br />
 
 		Adds [page:Vector3 offset] to both the upper and lower bounds of this box, effectively moving this box
-		[page:Vector3 offset] units in 2D space.
+		[page:Vector3 offset] units in 3D space.
 		</div>
 
 		<h3>[method:Box3 union]( [page:Box3 box] )</h3>

+ 1 - 1
docs/api/math/Color.html

@@ -35,7 +35,7 @@ var color = new THREE.Color( 'skyblue' );
 //HSL string
 var color = new THREE.Color("hsl(0, 100%, 50%)");
 
-//Seperate RGB values between 0 and 1
+//Separate RGB values between 0 and 1
 var color = new THREE.Color( 1, 0, 0 );
 		</code>
 

+ 1 - 1
docs/api/math/Sphere.html

@@ -84,7 +84,7 @@
 
 		<h3>[method:Box3 getBoundingBox]( [page:Box3 optionalTarget] )</h3>
 		<div>
-			[page:Box3 optionalTarget] - (optional) if specified, the result will be copied into this [page:Vector3], otherwise a new [page:Box3] will be created.<br /><br />
+			[page:Box3 optionalTarget] - (optional) if specified, the result will be copied into this [page:Box3], otherwise a new [page:Box3] will be created.<br /><br />
 
 			Returns a[link:https://en.wikipedia.org/wiki/Minimum_bounding_box Minimum Bounding Box] for the sphere.
 		</div>

+ 1 - 1
docs/api/math/Vector3.html

@@ -301,7 +301,7 @@ var d = a.distanceTo( b );
 		<div>Multiplies this vector by scalar [page:Float s].</div>
 
 		<h3>[method:Vector3 multiplyVectors]( [page:Vector3 a], [page:Vector3 b] )</h3>
-		<div>Sets this vector equal to [page:Vector3 a] x [page:Vector3 b].</div>
+		<div>Sets this vector equal to [page:Vector3 a] * [page:Vector3 b], component-wise.</div>
 
 		<h3>[method:Vector3 project]( [page:Camera camera] )</h3>
 		<div>

+ 1 - 1
docs/api/math/Vector4.html

@@ -176,7 +176,7 @@ var d = a.dot( b );
 
 		<h3>[method:Float getComponent]( [page:Integer index] )</h3>
 		<div>
-		[page:Integer index] - 0, 1 or 2.<br /><br />
+		[page:Integer index] - 0, 1, 2 or 3.<br /><br />
 
 		If index equals 0 returns the [page:.x x] value. <br />
 		If index equals 1 returns the [page:.y y] value. <br />

+ 1 - 1
docs/api/objects/Mesh.html

@@ -67,7 +67,7 @@
 
 		<h3>[property:Material material]</h3>
 		<div>
-			An instance of material derived from the [page:Material] base class, defining the
+			An instance of material derived from the [page:Material] base class or an array of materials, defining the
 			object's appearance. Default is a [page:MeshBasicMaterial] with a random colour.
 		</div>
 

+ 1 - 1
docs/api/renderers/WebGLRenderTarget.html

@@ -34,7 +34,7 @@
 
 		[page:Constant wrapS] - default is [page:Textures ClampToEdgeWrapping]. <br />
 		[page:Constant wrapT] - default is [page:Textures ClampToEdgeWrapping]. <br />
-		[page:Constant magFilter] - default is [page:Textures .LinearFilter]. <br />
+		[page:Constant magFilter] - default is [page:Textures LinearFilter]. <br />
 		[page:Constant minFilter] - default is [page:Textures LinearFilter]. <br />
 		[page:Constant format] - default is [page:Textures RGBAFormat]. <br />
 		[page:Constant type] - default is [page:Textures UnsignedByteType]. <br />

+ 5 - 2
docs/api/renderers/WebGLRenderer.html

@@ -51,7 +51,10 @@
 		Default is *true*.<br />
 
 		[page:Boolean preserveDrawingBuffer] - whether to preserve the buffers until manually cleared
-	  or overwritten. Default is *false*.<br />
+		or overwritten. Default is *false*.<br />
+		
+		[page:String powerPreference] - Provides a hint to the user agent indicating what configuration
+		of GPU is suitable for this WebGL context. Can be *"high-performance"*, *"low-power"* or *"default"*. Default is *"default"*.<br />
 
 		[page:Boolean depth] - whether the drawing buffer has a
 		[link:https://en.wikipedia.org/wiki/Z-buffering depth buffer] of at least 16 bits.
@@ -129,7 +132,7 @@
 		<h3>[property:WebGLRenderingContext context]</h3>
 		<div>
 		The renderer obtains a [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext RenderingContext] context
-		  from its [page:WebGLRenderer.domElement domElement][page:WebGLRenderer.domElement domElement] by default, using
+		  from its [page:WebGLRenderer.domElement domElement] by default, using
 			[link:https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/getContext HTMLCanvasElement.getContext]().<br /><br />
 
 		You can create this manually, however it must correspond to the

+ 49 - 49
docs/api/scenes/Fog.html

@@ -1,50 +1,50 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
 		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">This class contains the parameters that define linear fog, i.e., that grows linearly denser with the distance.</div>
-
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]( [page:Integer hex], [page:Float near], [page:Float far] )</h3>
-		<div>The hex parameter is passed to the [page:Color] constructor to set the color property.  Hex can be a hexadecimal integer or a CSS-style string.</div>
-
-		<h2>Properties</h2>
-
-		<h3>[property:String name]</h3>
-		<div>Optional name of the object (doesn't need to be unique). Default is an empty string.</div>
-
-		<h3>[property:Color color]</h3>
-		<div>Fog color.  Example: If set to black, far away objects will be rendered black.</div>
-
-		<h3>[property:Float near]</h3>
-		<div>The minimum distance to start applying fog. Objects that are less than 'near' units from the active camera won't be affected by fog.</div>
-		<div>Default is 1.</div>
-
-		<h3>[property:Float far]</h3>
-		<div>The maximum distance at which fog stops being calculated and applied. Objects that are more than 'far' units away from the active camera won't be affected by fog.</div>
-		<div>Default is 1000.</div>
-
-		<h2>Methods</h2>
-
-		<h3>[method:Fog clone]()</h3>
-		<div>Returns a new fog instance with the same parameters as this one.</div>
-
-		<h3>[method:Fog toJSON]()</h3>
-		<div>Return fog data in JSON format.</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">This class contains the parameters that define linear fog, i.e., that grows linearly denser with the distance.</div>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]( [page:Integer color], [page:Float near], [page:Float far] )</h3>
+		<div>The color parameter is passed to the [page:Color] constructor to set the color property. Color can be a hexadecimal integer or a CSS-style string.</div>
+
+		<h2>Properties</h2>
+
+		<h3>[property:String name]</h3>
+		<div>Optional name of the object (doesn't need to be unique). Default is an empty string.</div>
+
+		<h3>[property:Color color]</h3>
+		<div>Fog color.  Example: If set to black, far away objects will be rendered black.</div>
+
+		<h3>[property:Float near]</h3>
+		<div>The minimum distance to start applying fog. Objects that are less than 'near' units from the active camera won't be affected by fog.</div>
+		<div>Default is 1.</div>
+
+		<h3>[property:Float far]</h3>
+		<div>The maximum distance at which fog stops being calculated and applied. Objects that are more than 'far' units away from the active camera won't be affected by fog.</div>
+		<div>Default is 1000.</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:Fog clone]()</h3>
+		<div>Returns a new fog instance with the same parameters as this one.</div>
+
+		<h3>[method:Fog toJSON]()</h3>
+		<div>Return fog data in JSON format.</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 2
docs/api/scenes/FogExp2.html

@@ -16,9 +16,9 @@
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( [page:Integer hex], [page:Float density] )</h3>
+		<h3>[name]( [page:Integer color], [page:Float density] )</h3>
 
-		<div>The hex parameter is passed to the [page:Color] constructor to set the color property.  Hex can be a hexadecimal integer or a CSS-style string.</div>
+		<div>The color parameter is passed to the [page:Color] constructor to set the color property. Color can be a hexadecimal integer or a CSS-style string.</div>
 		<h2>Properties</h2>
 
 		<h3>[property:String name]</h3>

+ 27 - 0
docs/api/textures/DataTexture.html

@@ -32,6 +32,33 @@
 			In order to use the types THREE.FloatType and THREE.HalfFloatType, the WebGL implementation must support the respective extensions OES_texture_float and OES_texture_half_float. In order to use THREE.LinearFilter for component-wise, bilinear interpolation of the texels based on these types, the WebGL extensions OES_texture_float_linear or OES_texture_half_float_linear must also be present.
 		</div>
 
+		<h2>Example</h2>
+
+		<code>
+		// create a buffer with color data
+
+		var size = width * height;
+		var data = new Uint8Array( 3 * size );
+
+		var r = Math.floor( color.r * 255 );
+		var g = Math.floor( color.g * 255 );
+		var b = Math.floor( color.b * 255 );
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			var stride = i * 3;
+
+			data[ stride ] = r;
+			data[ stride + 1 ] = g;
+			data[ stride + 2 ] = b;
+
+		}
+
+		// used the buffer to create a [name]
+
+		var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
+		</code>
+
 		<h2>Properties</h2>
 
 		<h3>[property:Image image]</h3>

+ 1 - 1
docs/api/textures/Texture.html

@@ -15,7 +15,7 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )</h3>
+		<h3>[name]( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding )</h3>
 
 		<h2>Example</h2>
 

+ 2 - 0
docs/examples/exporters/GLTFExporter.html

@@ -79,6 +79,8 @@ exporter.parse( [ scene1, object1, object2, scene2 ], ...)
 			<li>onlyVisible - bool. Export only visible objects. Default is true.</li>
 			<li>truncateDrawRange - bool. Export just the attributes within the drawRange, if defined, instead of exporting the whole array. Default is true.</li>
 			<li>binary - bool. Export in binary (.glb) format, returning an ArrayBuffer. Default is false.</li>
+			<li>embedImages - bool. Export with images embedded into the glTF asset. Default is true.</li>
+			<li>animations - Array<[page:AnimationClip AnimationClip]>. List of animations to be included in the export.
 		</ul>
 		</div>
 		<div>

+ 19 - 9
docs/examples/loaders/GLTFLoader.html

@@ -48,39 +48,49 @@
 		<code>
 			// Instantiate a loader
 			var loader = new THREE.GLTFLoader();
-	
+
 			// Load a glTF resource
 			loader.load(
 				// resource URL
 				'models/gltf/duck/duck.gltf',
 				// called when the resource is loaded
 				function ( gltf ) {
-	
+
 					scene.add( gltf.scene );
-	
+
 					gltf.animations; // Array&lt;THREE.AnimationClip&gt;
 					gltf.scene; // THREE.Scene
 					gltf.scenes; // Array&lt;THREE.Scene&gt;
 					gltf.cameras; // Array&lt;THREE.Camera&gt;
-	
+
 				},
 				// called when loading is in progresses
 				function ( xhr ) {
-	
+
 					console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
-	
+
 				},
 				// called when loading has errors
 				function ( error ) {
-	
+
 					console.log( 'An error happened' );
-	
+
 				}
 			);
 			</code>
 
 		[example:webgl_loader_gltf]
 
+		<h2>Browser compatibility</h2>
+
+		<p>GLTFLoader relies on ES6 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>,
+		which are not supported in IE11. To use the loader in IE11, you must
+		<a href="https://github.com/stefanpenner/es6-promise">include a polyfill</a>
+		providing a Promise replacement.</p>
+
+		<br>
+		<hr>
+
 		<h2>Constructor</h2>
 
 		<h3>[name]( [page:LoadingManager manager] )</h3>
@@ -99,7 +109,7 @@
 		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
 		<div>
 		[page:String url] — A string containing the path/URL of the <em>.gltf</em> or <em>.glb</em> file.<br />
-		[page:Function onLoad] — (optional) A function to be called after the loading is successfully completed. The function receives the loaded JSON response returned from [page:Function parse].<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed. The function receives the loaded JSON response returned from [page:Function parse].<br />
 		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
 		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
 		</div>

+ 12 - 11
docs/examples/loaders/LoaderSupport.html

@@ -156,10 +156,9 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null validate] ( [page:Function functionCodeBuilder], [page:Boolean forceWorkerReload], Array of [page:String libLocations], [page:String libPath], [page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] )</h3>
+		<h3>[method:null validate] ( [page:Function functionCodeBuilder], Array of [page:String libLocations], [page:String libPath], [page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] )</h3>
 		<div>
 			[page:Function functionCodeBuilder] - Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons.<br>
-			[page:Boolean forceWorkerReload] - Force re-build of the worker code.<br>
 			Array of [page:String libLocations] - URL of libraries that shall be added to worker code relative to libPath.<br>
 			[page:String libPath] - Base path used for loading libraries.<br>
 			[page:LoaderSupport.WorkerRunnerRefImpl runnerImpl] - The default worker parser wrapper implementation (communication and execution). An extended class could be passed here.
@@ -178,12 +177,6 @@
 		</div>
 
 
-		<h3>[method:null terminateWorker] ()</h3>
-		<div>
-			Terminate the worker and the code.
-		</div>
-
-
 		<h3>[method:null setCallbacks] ( [page:Function builder], [page:Function onLoad] )</h3>
 		<div>
 			[page:Function builder] - The builder function. Default is [page:LoaderSupport.Builder].<br>
@@ -251,7 +244,7 @@
 			- prepareWorkers<br>
 			- enqueueForRun<br>
 			- processQueue<br>
-			- deregister
+			- tearDown
 		</div>
 
 
@@ -283,7 +276,10 @@
 		</div>
 
 
-		<h3>[method:null deregister]()</h3>
+		<h3>[method:null tearDown]( [page:Function callbackOnFinishedProcessing] )</h3>
+		<div>
+			[page:Function callbackOnFinishedProcessing] - Function called once all workers finished processing.
+		</div>
 		<div>
 			Terminate all workers.
 		</div>
@@ -300,6 +296,11 @@
 			Returns the maximum number of workers.
 		</div>
 
+		<h3>[method:Boolean isRunning]()</h3>
+		<div>
+			Returns if any workers are running.
+		</div>
+
 
 		<h3>[method:null setCrossOrigin]( [page:String crossOrigin] )</h3>
 		<div>
@@ -543,7 +544,7 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null isValid]( [page:Object input] )</h3>
+		<h3>[method:Boolean isValid]( [page:Object input] )</h3>
 		<div>
 			[page:Object input] - Can be anything
 		</div>

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

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

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

@@ -76,7 +76,7 @@
 			[page:String url] - A string containing the path/URL of the <em>.obj</em> file.<br>
 			[page:Function onLoad] - A function to be called after loading is successfully completed. The function receives loaded [page:Object3D] as an argument.<br>
 			[page:Function onProgress] - (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes.<br>
-			[page:Function onError] - (optional) A function to be called if an error occurrs during loading. The function receives the error as an argument.<br>
+			[page:Function onError] - (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br>
 			[page:Function onMeshAlter] - (optional) A function to be called after a new mesh raw data becomes available for alteration.<br>
 			[page:boolean useAsync] - (optional) If true, uses async loading with worker, if false loads data synchronously.
 		</div>

+ 7 - 1
docs/examples/renderers/CanvasRenderer.html

@@ -55,7 +55,13 @@
         <div>parameters is an optional object with properties defining the renderer's behaviour. The constructor also accepts no parameters at all. In all cases, it will assume sane defaults when parameters are missing.</div>
 
 		<div>
-		canvas — A [page:Canvas] where the renderer draws its output.
+		[page:DOMElement canvas] - A [link:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas canvas]
+		where the renderer draws its output.
+		This corresponds to the [page:CanvasRenderer.domElement domElement] property below.
+		If not passed in here, a new canvas element will be created.<br />
+
+		[page:Boolean alpha] - whether the canvas contains an alpha (transparency) buffer or not.
+	  Default is *false*.
 		</div>
 
 

+ 5 - 1
docs/list.js

@@ -5,6 +5,7 @@ var list = {
 		"Getting Started": {
 			"Creating a scene": "manual/introduction/Creating-a-scene",
 			"Import via modules": "manual/introduction/Import-via-modules",
+			"Browser support": "manual/introduction/Browser-support",
 			"WebGL compatibility check": "manual/introduction/WebGL-compatibility-check",
 			"How to run things locally": "manual/introduction/How-to-run-thing-locally",
 			"Drawing Lines": "manual/introduction/Drawing-lines",
@@ -103,6 +104,7 @@ var list = {
 		},
 
 		"Extras": {
+			"Earcut": "api/extras/Earcut",
 			"SceneUtils": "api/extras/SceneUtils",
 			"ShapeUtils": "api/extras/ShapeUtils"
 		},
@@ -174,8 +176,8 @@ var list = {
 			"TorusGeometry": "api/geometries/TorusGeometry",
 			"TorusKnotBufferGeometry": "api/geometries/TorusKnotBufferGeometry",
 			"TorusKnotGeometry": "api/geometries/TorusKnotGeometry",
-			"TubeGeometry": "api/geometries/TubeGeometry",
 			"TubeBufferGeometry": "api/geometries/TubeBufferGeometry",
+			"TubeGeometry": "api/geometries/TubeGeometry",
 			"WireframeGeometry": "api/geometries/WireframeGeometry"
 		},
 
@@ -224,9 +226,11 @@ var list = {
 			"DataTextureLoader": "api/loaders/DataTextureLoader",
 			"FileLoader": "api/loaders/FileLoader",
 			"FontLoader": "api/loaders/FontLoader",
+			"ImageBitmapLoader": "api/loaders/ImageBitmapLoader",
 			"ImageLoader": "api/loaders/ImageLoader",
 			"JSONLoader": "api/loaders/JSONLoader",
 			"Loader": "api/loaders/Loader",
+			"LoaderUtils": "api/loaders/LoaderUtils",
 			"MaterialLoader": "api/loaders/MaterialLoader",
 			"ObjectLoader": "api/loaders/ObjectLoader",
 			"TextureLoader": "api/loaders/TextureLoader"

+ 1 - 0
docs/manual/introduction/Animation-system.html

@@ -110,6 +110,7 @@
 				<li>[page:JSONLoader THREE.JSONLoader]</li>
 				<li>[page:ObjectLoader THREE.ObjectLoader]</li>
 				<li>THREE.BVHLoader</li>
+				<li>THREE.ColladaLoader</li>
 				<li>THREE.FBXLoader</li>
 				<li>[page:GLTFLoader THREE.GLTFLoader]</li>
 				<li>THREE.MMDLoader</li>

+ 123 - 0
docs/manual/introduction/Browser-support.html

@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="utf-8">
+	<base href="../../" />
+	<script src="list.js"></script>
+	<script src="page.js"></script>
+	<link type="text/css" rel="stylesheet" href="page.css" />
+</head>
+<body>
+	<h1>[name]</h1>
+
+	<h2>Overview</h2>
+	<div>
+		<p>
+			Three.js can use WebGL to render your scenes on all modern browsers. For older browsers, especially Internet Explorer 10 and below, you may have to fallback to one of the other [link:https://github.com/mrdoob/three.js/tree/master/examples/js/renderers renderers] (CSS2DRenderer, CSS3DRenderer, SVGRenderer, CanvasRenderer). Additionally, you may have to include some polyfills, especially if you are using files from the [link:https://github.com/mrdoob/three.js/tree/master/examples /examples] folder.
+		</p>
+		<p>
+			Note: if you don't need to support these old browsers, then it is not recommended to use the other renderers as they are slower and support less features than the WebGLRenderer.
+		</p>
+	</div>
+
+	<h2>Browsers that support WebGL</h2>
+	<div>
+		<p>
+			Google Chrome 9+, Firefox 4+, Opera 15+, Safari 5.1+, Internet Explorer 11 and Microsoft Edge. You can find which browsers support WebGL at [link:https://caniuse.com/#feat=webgl Can I use WebGL].
+		</p>
+	</div>
+
+	<h2>JavaScript Language Features or Web APIs Used in three.js</h2>
+	<div>
+		<p>
+			Here are some features used in three.js. Some of them may require additional polyfills.
+		</p>
+		<table>
+			<thead>
+				<tr>
+					<th>Feature</th>
+					<th>Use Scope</th>
+					<th>Modules</th>
+				</tr>
+			</thead>
+			<tbody>
+				<tr>
+					<td>Typed Arrays</td>
+					<td>Source</td>
+					<td>BufferAttribute, BufferGeometry, etc.</td>
+				</tr>
+				<tr>
+					<td>Web Audio API</td>
+					<td>Source</td>
+					<td>Audio, AudioContext, AudioListener, etc.</td>
+				</tr>
+				<tr>
+					<td>WebVR API</td>
+					<td>Source</td>
+					<td>WebVRManager, etc.</td>
+				</tr>
+				<tr>
+					<td>Blob</td>
+					<td>Source</td>
+					<td>FileLoader, etc.</td>
+				</tr>
+				<tr>
+					<td>Promise</td>
+					<td>Examples</td>
+					<td>GLTFLoader, GLTF2Loader, WebVR, VREffect, etc.</td>
+				</tr>
+				<tr>
+					<td>Fetch</td>
+					<td>Examples</td>
+					<td>ImageBitmapLoader, etc.</td>
+				</tr>
+				<tr>
+					<td>File API</td>
+					<td>Examples</td>
+					<td>GLTFExporter, etc.</td>
+				</tr>
+				<tr>
+					<td>URL API</td>
+					<td>Examples</td>
+					<td>GLTFLoader, etc.</td>
+				</tr>
+				<tr>
+					<td>Pointer Lock API</td>
+					<td>Examples</td>
+					<td>PointerLockControls</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+
+	<h2>Polyfills</h2>
+	<div>
+		<p>Just import polyfills based on your requirements. Taking IE9 as an example, you need to polyfill at least these features:</p>
+		<ul>
+			<li>Typed Arrays</li>
+			<li>Blob</li>
+		</ul>
+	</div>
+
+	<h3>Suggested polyfills</h3>
+	<div>
+		<ul>
+			<li>
+				[link:https://github.com/zloirock/core-js core-js]
+			</li>
+			<li>
+				[link:https://github.com/inexorabletash/polyfill/blob/master/typedarray.js typedarray.js]
+			</li>
+			<li>
+				[link:https://github.com/stefanpenner/es6-promise/ ES6-Promise]
+			</li>
+			<li>
+				[link:https://github.com/eligrey/Blob.js Blob.js]
+			</li>
+			<li>
+				[link:https://github.com/github/fetch fetch]
+			</li>
+		</ul>
+	</div>
+</body>
+</html>

+ 2 - 2
docs/manual/introduction/Creating-a-scene.html

@@ -100,7 +100,7 @@
 		animate();
 		</code>
 
-		<div>This will create a loop that causes the renderer to draw the scene 60 times per second. If you're new to writing games in the browser, you might say <em>"why don't we just create a setInterval ?"</em> The thing is - we could, but <strong>requestAnimationFrame</strong> has a number of advantages. Perhaps the most important one is that it pauses when the user navigates to another browser tab, hence not wasting their precious processing power and battery life.</div>
+		<div>This will create a loop that causes the renderer to draw the scene every time the screen is refreshed (on a typical screen this means 60 times per second). If you're new to writing games in the browser, you might say <em>"why don't we just create a setInterval ?"</em> The thing is - we could, but <strong>requestAnimationFrame</strong> has a number of advantages. Perhaps the most important one is that it pauses when the user navigates to another browser tab, hence not wasting their precious processing power and battery life.</div>
 
 		<h2>Animating the cube</h2>
 
@@ -113,7 +113,7 @@
 		cube.rotation.y += 0.1;
 		</code>
 
-		<div>This will be run every frame (60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animate loop. You can of course call other functions from there, so that you don't end up with a <strong>animate</strong> function that's hundreds of lines.
+		<div>This will be run every frame (normally 60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animate loop. You can of course call other functions from there, so that you don't end up with a <strong>animate</strong> function that's hundreds of lines.
 		</div>
 
 		<h2>The result</h2>

+ 1 - 1
docs/page.js

@@ -63,7 +63,7 @@ function onDocumentLoad( event ) {
 	text = text.replace( /\[(?:member|property|method):([\w]+) ([\w\.\s]+)\]/gi, "<a onclick=\"window.parent.setUrlFragment('" + name + ".$2')\" target=\"_parent\" title=\"" + name + ".$2\" class=\"permalink\">#</a> .<a onclick=\"window.parent.setUrlFragment('$1')\" title=\"$1\" id=\"$2\">$2</a> " );
 
 	text = text.replace( /\[link:([\w|\:|\/|\.|\-|\_]+)\]/gi, "[link:$1 $1]" ); // [link:url] to [link:url title]
-	text = text.replace( /\[link:([\w|\:|\/|\.|\-|\_|\(|\)|\#]+) ([\w|\:|\/|\.|\-|\_|\s]+)\]/gi, "<a href=\"$1\"  target=\"_blank\">$2</a>" ); // [link:url title]
+	text = text.replace( /\[link:([\w|\:|\/|\.|\-|\_|\(|\)|\#|\=]+) ([\w|\:|\/|\.|\-|\_|\s]+)\]/gi, "<a href=\"$1\"  target=\"_blank\">$2</a>" ); // [link:url title]
 	text = text.replace( /\*([\w|\d|\"|\-|\(][\w|\d|\ |\-|\/|\+|\-|\(|\)|\=|\,|\.\"]*[\w|\d|\"|\)]|\w)\*/gi, "<strong>$1</strong>" ); // *
 
 	text = text.replace( /\[example:([\w\_]+)\]/gi, "[example:$1 $1]" ); // [example:name] to [example:name title]

+ 1 - 63
editor/index.html

@@ -120,6 +120,7 @@
 		<script src="js/Sidebar.Scene.js"></script>
 		<script src="js/Sidebar.Project.js"></script>
 		<script src="js/Sidebar.Settings.js"></script>
+		<script src="js/Sidebar.Settings.Shortcuts.js"></script>
 		<script src="js/Sidebar.Properties.js"></script>
 		<script src="js/Sidebar.Object.js"></script>
 		<script src="js/Sidebar.Geometry.js"></script>
@@ -177,8 +178,6 @@
 			window.URL = window.URL || window.webkitURL;
 			window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
 
-			const IS_MAC = navigator.platform.toUpperCase().indexOf( 'MAC' ) >= 0;
-
 			Number.prototype.format = function (){
 				return this.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
 			};
@@ -306,67 +305,6 @@
 
 			}, false );
 
-			document.addEventListener( 'keydown', function ( event ) {
-
-				switch ( event.keyCode ) {
-
-					case 8: // backspace
-
-						event.preventDefault(); // prevent browser back
-
-					case 46: // delete
-
-						var object = editor.selected;
-
-						if ( confirm( 'Delete ' + object.name + '?' ) === false ) return;
-
-						var parent = object.parent;
-						if ( parent !== null ) editor.execute( new RemoveObjectCommand( object ) );
-
-						break;
-
-					case 90: // Register Ctrl-Z for Undo, Ctrl-Shift-Z for Redo
-
-						if ( IS_MAC ? event.metaKey : event.ctrlKey ) {
-
-							event.preventDefault(); // Prevent Safari from opening/closing tabs
-
-							if ( event.shiftKey ) {
-
-								editor.redo();
-
-							} else {
-
-								editor.undo();
-
-							}
-
-						}
-
-						break;
-
-					case 87: // Register W for translation transform mode
-
-						editor.signals.transformModeChanged.dispatch( 'translate' );
-
-						break;
-
-					case 69: // Register E for rotation transform mode
-
-						editor.signals.transformModeChanged.dispatch( 'rotate' );
-
-						break;
-
-					case 82: // Register R for scaling transform mode
-
-						editor.signals.transformModeChanged.dispatch( 'scale' );
-
-						break;
-
-				}
-
-			}, false );
-
 			function onWindowResize( event ) {
 
 				editor.signals.windowResize.dispatch();

+ 7 - 1
editor/js/Config.js

@@ -19,7 +19,13 @@ var Config = function ( name ) {
 
 		'project/vr': false,
 
-		'settings/history': false
+		'settings/history': false,
+
+		'settings/shortcuts/translate': 'w',
+		'settings/shortcuts/rotate': 'e',
+		'settings/shortcuts/scale': 'r',
+		'settings/shortcuts/undo': 'z',
+		'settings/shortcuts/focus': 'f'
 	};
 
 	if ( window.localStorage[ name ] === undefined ) {

+ 164 - 0
editor/js/Sidebar.Settings.Shortcuts.js

@@ -0,0 +1,164 @@
+/**
+ * @author TyLindberg / https://github.com/TyLindberg
+ */
+
+Sidebar.Settings.Shortcuts = function ( editor ) {
+
+	const IS_MAC = navigator.platform.toUpperCase().indexOf( 'MAC' ) >= 0;
+
+	function isValidKeyBinding( key ) {
+
+		return key.match( /^[A-Za-z0-9]$/i ); // Can't use z currently due to undo/redo
+
+	}
+
+	var config = editor.config;
+	var signals = editor.signals;
+
+	var container = new UI.Div();
+	container.add( new UI.Break() );
+
+	var shortcuts = [ 'translate', 'rotate', 'scale', 'undo', 'focus' ];
+
+	for ( var i = 0; i < shortcuts.length; i ++ ) {
+
+		let name = shortcuts[ i ];
+
+		let configName = 'settings/shortcuts/' + name;
+		let shortcutRow = new UI.Row();
+
+		let shortcutInput = new UI.Input().setWidth( '150px' ).setFontSize( '12px' );
+		shortcutInput.setTextTransform( 'lowercase' );
+		shortcutInput.onChange( function () {
+
+			var value = shortcutInput.getValue().toLowerCase();
+
+			if ( isValidKeyBinding( value ) ) {
+
+				config.setKey( configName, value );
+
+			}
+
+		} );
+
+		// Automatically highlight when selecting an input field
+		shortcutInput.dom.addEventListener( 'click', function () {
+
+			shortcutInput.dom.select();
+
+		} );
+
+		// If the value of the input field is invalid, revert the input field
+		// to contain the key binding stored in config
+		shortcutInput.dom.addEventListener( 'blur', function () {
+
+			if ( ! isValidKeyBinding( shortcutInput.getValue() ) ) {
+
+				shortcutInput.setValue( config.getKey( configName ) );
+
+			}
+
+		} );
+
+		// If a valid key binding character is entered, blur the input field
+		shortcutInput.dom.addEventListener( 'keyup', function ( event ) {
+
+			if ( isValidKeyBinding( event.key ) ) {
+
+				shortcutInput.dom.blur();
+
+			}
+
+		} );
+
+		if ( config.getKey( configName ) !== undefined ) {
+
+			shortcutInput.setValue( config.getKey( configName ) );
+
+		}
+
+		shortcutInput.dom.maxLength = 1;
+		shortcutRow.add( new UI.Text( name ).setTextTransform( 'capitalize' ).setWidth( '90px' ) );
+		shortcutRow.add( shortcutInput );
+
+		container.add( shortcutRow );
+
+	}
+
+	document.addEventListener( 'keydown', function ( event ) {
+
+		switch ( event.key.toLowerCase() ) {
+
+			case 'backspace':
+
+				event.preventDefault(); // prevent browser back
+
+				// fall-through
+
+			case 'delete':
+
+				var object = editor.selected;
+
+				if ( object === null ) return;
+				if ( confirm( 'Delete ' + object.name + '?' ) === false ) return;
+
+				var parent = object.parent;
+				if ( parent !== null ) editor.execute( new RemoveObjectCommand( object ) );
+
+				break;
+
+			case config.getKey( 'settings/shortcuts/translate' ):
+
+				signals.transformModeChanged.dispatch( 'translate' );
+
+				break;
+
+			case config.getKey( 'settings/shortcuts/rotate' ):
+
+				signals.transformModeChanged.dispatch( 'rotate' );
+
+				break;
+
+			case config.getKey( 'settings/shortcuts/scale' ):
+
+				signals.transformModeChanged.dispatch( 'scale' );
+
+				break;
+
+			case config.getKey( 'settings/shortcuts/undo' ):
+
+				if ( IS_MAC ? event.metaKey : event.ctrlKey ) {
+
+					event.preventDefault(); // Prevent browser specific hotkeys
+
+					if ( event.shiftKey ) {
+
+						editor.redo();
+
+					} else {
+
+						editor.undo();
+
+					}
+
+				}
+
+				break;
+
+			case config.getKey( 'settings/shortcuts/focus' ):
+
+				if ( editor.selected !== null ) {
+
+					editor.focus( editor.selected );
+
+				}
+
+				break;
+
+		}
+
+	}, false );
+
+	return container;
+
+};

+ 2 - 0
editor/js/Sidebar.Settings.js

@@ -42,6 +42,8 @@ Sidebar.Settings = function ( editor ) {
 
 	container.add( themeRow );
 
+	container.add( new Sidebar.Settings.Shortcuts( editor ) );
+
 	return container;
 
 };

+ 1 - 43
editor/js/libs/tern-threejs/threejs.js

@@ -805,44 +805,6 @@
       "prototype": {},
       "!doc": "Contains handy functions geometry manipulations."
     },
-    "ImageUtils": {
-      "!url": "http://threejs.org/docs/#Reference/extras/ImageUtils",
-      "prototype": {
-        "crossOrigin": {
-          "!type": "string",
-          "!doc": "The crossOrigin string to implement CORS for loading the image from a different domain that allows CORS."
-        },
-        "generateDataTexture": {
-          "!type": "fn(width: number, height: number, color: number) -> +THREE.DataTexture",
-          "!doc": "Generates a texture of a single color. It is a DataTexture with format, RGBFormat."
-        },
-        "parseDDS": {
-          "!type": "fn(buffer: string, loadMipmaps: boolean) -> +THREE.CompressedTexture",
-          "!doc": "Parses a DDS Image from the string into a CompressedTexture."
-        },
-        "loadCompressedTexture": {
-          "!type": "fn(url: todo, mapping: todo, onLoad: todo, onError: todo) -> todo",
-          "!doc": "todo"
-        },
-        "loadTexture": {
-          "!type": "fn(url: string, mapping: UVMapping, onLoad: function, onError: function) -> todo",
-          "!doc": "todo"
-        },
-        "getNormalMap": {
-          "!type": "fn(image: todo, depth: todo) -> todo",
-          "!doc": "todo"
-        },
-        "loadCompressedTextureCube": {
-          "!type": "fn(array: todo, mapping: todo, onLoad: todo, onError: todo) -> todo",
-          "!doc": "todo"
-        },
-        "loadTextureCube": {
-          "!type": "fn(array: todo, mapping: todo, onLoad: todo, onError: todo) -> todo",
-          "!doc": "todo"
-        }
-      },
-      "!doc": "A Helper class to ease the loading of images of different types."
-    },
     "SceneUtils": {
       "!url": "http://threejs.org/docs/#Reference/extras/SceneUtils",
       "prototype": {
@@ -1852,10 +1814,6 @@
         "initMaterials": {
           "!type": "fn(materials: [], texturePath: string) -> []",
           "!doc": "Creates an array of [page:Material] based on the array of parameters m. The index of the parameters decide the correct index of the materials."
-        },
-        "extractUrlBase": {
-          "!type": "fn(url: string) -> string",
-          "!doc": "Extract the base from the URL."
         }
       },
       "!doc": "Base class for implementing loaders.",
@@ -5137,7 +5095,7 @@
         },
         "image": {
           "!type": "Image",
-          "!doc": "An Image object, typically created using the ImageUtils or [page:ImageLoader ImageLoader] classes. The Image object can include an image (e.g., PNG, JPG, GIF, DDS), video (e.g., MP4, OGG/OGV), or set of six images for a cube map. To use video as a texture you need to have a playing HTML5 video element as a source for your texture image and continuously update this texture as long as video is playing."
+          "!doc": "An Image object, typically created using the [page:ImageLoader ImageLoader] class. The Image object can include an image (e.g., PNG, JPG, GIF, DDS), video (e.g., MP4, OGG/OGV), or set of six images for a cube map. To use video as a texture you need to have a playing HTML5 video element as a source for your texture image and continuously update this texture as long as video is playing."
         },
         "mapping": {
           "!type": "object",

+ 4 - 2
examples/canvas_geometry_panorama.html

@@ -81,8 +81,10 @@
 
 				];
 
-				mesh = new THREE.Mesh( new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 ), materials );
-				mesh.scale.x = - 1;
+				var geometry = new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 );
+				geometry.scale( - 1, 1, 1 );
+
+				mesh = new THREE.Mesh( geometry, materials );
 				scene.add( mesh );
 
 				renderer = new THREE.CanvasRenderer();

+ 4 - 2
examples/canvas_geometry_panorama_fisheye.html

@@ -81,8 +81,10 @@
 
 				];
 
-				mesh = new THREE.Mesh( new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 ), materials );
-				mesh.scale.x = - 1;
+				var geometry = new THREE.BoxGeometry( 300, 300, 300, 7, 7, 7 );
+				geometry.scale( - 1, 1, 1 );
+
+				mesh = new THREE.Mesh( geometry, materials );
 				scene.add( mesh );
 
 				for ( var i = 0, l = mesh.geometry.vertices.length; i < l; i ++ ) {

+ 9 - 6
examples/files.js

@@ -42,8 +42,6 @@ var files = {
 		"webgl_geometry_terrain_fog",
 		"webgl_geometry_terrain_raycast",
 		"webgl_geometry_text",
-		"webgl_geometry_text_earcut",
-		"webgl_geometry_text_pnltri",
 		"webgl_geometry_text_shapes",
 		"webgl_gpgpu_birds",
 		"webgl_gpgpu_water",
@@ -90,11 +88,13 @@ var files = {
 		"webgl_loader_ctm_materials",
 		"webgl_loader_draco",
 		"webgl_loader_fbx",
+		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_json_blender",
 		"webgl_loader_json_claraio",
 		"webgl_loader_json_objconverter",
+		"webgl_loader_kmz",
 		"webgl_loader_md2",
 		"webgl_loader_md2_control",
 		"webgl_loader_mmd",
@@ -113,7 +113,6 @@ var files = {
 		"webgl_loader_playcanvas",
 		"webgl_loader_ply",
 		"webgl_loader_prwm",
-		"webgl_loader_ttf",
 		"webgl_loader_sea3d",
 		"webgl_loader_sea3d_hierarchy",
 		"webgl_loader_sea3d_keyframe",
@@ -122,6 +121,11 @@ var files = {
 		"webgl_loader_sea3d_skinning",
 		"webgl_loader_sea3d_sound",
 		"webgl_loader_stl",
+		"webgl_loader_texture_exr",
+		"webgl_loader_texture_hdr",
+		"webgl_loader_texture_pvrtc",
+		"webgl_loader_texture_tga",
+		"webgl_loader_ttf",
 		"webgl_loader_utf8",
 		"webgl_loader_vrml",
 		"webgl_loader_vtk",
@@ -158,11 +162,8 @@ var files = {
 		"webgl_materials_texture_canvas",
 		"webgl_materials_texture_compressed",
 		"webgl_materials_texture_filters",
-		"webgl_materials_texture_hdr",
 		"webgl_materials_texture_manualmipmap",
-		"webgl_materials_texture_pvrtc",
 		"webgl_materials_texture_rotation",
-		"webgl_materials_texture_tga",
 		"webgl_materials_transparency",
 		"webgl_materials_variations_basic",
 		"webgl_materials_variations_lambert",
@@ -181,6 +182,7 @@ var files = {
 		"webgl_morphtargets",
 		"webgl_morphtargets_horse",
 		"webgl_morphtargets_human",
+		"webgl_morphtargets_sphere",
 		"webgl_multiple_canvases_circle",
 		"webgl_multiple_canvases_complex",
 		"webgl_multiple_canvases_grid",
@@ -315,6 +317,7 @@ var files = {
 	],
 	"misc": [
 		"misc_animation_authoring",
+		"misc_animation_groups",
 		"misc_animation_keys",
 		"misc_controls_deviceorientation",
 		"misc_controls_fly",

+ 3 - 3
examples/index.html

@@ -237,7 +237,7 @@
 
 		</script>
 
-		<div id="panel" class="collapsed">
+		<div id="panel">
 			<h1><a href="http://threejs.org">three.js</a> / examples</h1>
 			<a id="expandButton" href="#">
 				<span></span>
@@ -273,8 +273,8 @@
 
 		var expandButton = document.getElementById( 'expandButton' );
 		expandButton.addEventListener( 'click', function ( event ) {
-			panel.classList.toggle( 'collapsed' );
 			event.preventDefault();
+			panel.classList.remove( 'collapsed' );
 		} );
 
 		// iOS iframe auto-resize workaround
@@ -359,7 +359,7 @@
 			window.location.hash = file;
 			viewer.focus();
 
-			panel.classList.toggle( 'collapsed' );
+			panel.classList.add( 'collapsed' );
 
 			selected = file;
 

+ 1 - 1
examples/js/QuickHull.js

@@ -470,7 +470,7 @@
 
 				// 3. The next vertex 'v3' is the one farthest to the plane 'v0', 'v1', 'v2'
 
-				maxDistance = -1;
+				maxDistance = - 1;
 				plane.setFromCoplanarPoints( v0.point, v1.point, v2.point );
 
 				for ( i = 0, l = this.vertices.length; i < l; i ++ ) {

+ 260 - 38
examples/js/exporters/GLTFExporter.js

@@ -39,6 +39,13 @@ var THREE_TO_WEBGL = {
 	1008: WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR
 };
 
+var PATH_PROPERTIES = {
+	scale: 'scale',
+	position: 'translation',
+	quaternion: 'rotation',
+	morphTargetInfluences: 'weights'
+};
+
 //------------------------------------------------------------------------------
 // GLTF Exporter
 //------------------------------------------------------------------------------
@@ -53,19 +60,26 @@ THREE.GLTFExporter.prototype = {
 	 * @param  {THREE.Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
 	 * @param  {Function} onDone  Callback on completed
 	 * @param  {Object} options options
-	 *                          trs: Exports position, rotation and scale instead of matrix
-	 *                          binary: Exports `.glb` as ArrayBuffer, instead of `.gltf` as JSON
 	 */
 	parse: function ( input, onDone, options ) {
 
 		var DEFAULT_OPTIONS = {
 			trs: false,
 			onlyVisible: true,
-			truncateDrawRange: true
+			truncateDrawRange: true,
+			embedImages: true,
+			animations: []
 		};
 
 		options = Object.assign( {}, DEFAULT_OPTIONS, options );
 
+		if ( options.animations.length > 0 ) {
+
+			// Only TRS properties, and not matrices, may be targeted by animation.
+			options.trs = true;
+
+		}
+
 		var outputJSON = {
 
 			asset: {
@@ -79,6 +93,8 @@ THREE.GLTFExporter.prototype = {
 
 		var byteOffset = 0;
 		var dataViews = [];
+		var nodeMap = {};
+		var skins = [];
 		var cachedData = {
 
 			images: {},
@@ -86,6 +102,8 @@ THREE.GLTFExporter.prototype = {
 
 		};
 
+		var cachedCanvas;
+
 		/**
 		 * Compare two arrays
 		 */
@@ -118,9 +136,9 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
-			var buffer = new ArrayBuffer( text.length * 2 ); // 2 bytes per character.
+			var buffer = new ArrayBuffer( text.length );
 
-			var bufferView = new Uint16Array( buffer );
+			var bufferView = new Uint8Array( buffer );
 
 			for ( var i = 0; i < text.length; ++ i ) {
 
@@ -133,8 +151,8 @@ THREE.GLTFExporter.prototype = {
 		}
 
 		/**
-		 * Get the min and he max vectors from the given attribute
-		 * @param  {THREE.WebGLAttribute} attribute Attribute to find the min/max
+		 * Get the min and max vectors from the given attribute
+		 * @param  {THREE.BufferAttribute} attribute Attribute to find the min/max
 		 * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
 		 */
 		function getMinMax( attribute ) {
@@ -229,12 +247,14 @@ THREE.GLTFExporter.prototype = {
 
 		/**
 		 * Process and generate a BufferView
-		 * @param  {[type]} data [description]
-		 * @return {[type]}      [description]
+		 * @param  {THREE.BufferAttribute} data
+		 * @param  {number} componentType
+		 * @param  {number} start
+		 * @param  {number} count
+		 * @param  {number} target (Optional) Target usage of the BufferView
+		 * @return {Object}
 		 */
-		function processBufferView( data, componentType, start, count ) {
-
-			var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
+		function processBufferView( data, componentType, start, count, target ) {
 
 			if ( ! outputJSON.bufferViews ) {
 
@@ -251,12 +271,19 @@ THREE.GLTFExporter.prototype = {
 
 				buffer: processBuffer( data, componentType, start, count ),
 				byteOffset: byteOffset,
-				byteLength: byteLength,
-				byteStride: data.itemSize * componentSize,
-				target: isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER
+				byteLength: byteLength
 
 			};
 
+			if ( target !== undefined ) gltfBufferView.target = target;
+
+			if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) {
+
+				// Only define byteStride for vertex attributes.
+				gltfBufferView.byteStride = data.itemSize * componentSize;
+
+			}
+
 			byteOffset += byteLength;
 
 			outputJSON.bufferViews.push( gltfBufferView );
@@ -275,7 +302,8 @@ THREE.GLTFExporter.prototype = {
 
 		/**
 		 * Process attribute to generate an accessor
-		 * @param  {THREE.WebGLAttribute} attribute Attribute to process
+		 * @param  {THREE.BufferAttribute} attribute Attribute to process
+		 * @param  {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
 		 * @return {Integer}           Index of the processed accessor on the "accessors" array
 		 */
 		function processAccessor( attribute, geometry ) {
@@ -286,14 +314,15 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
-			var types = [
+			var types = {
 
-				'SCALAR',
-				'VEC2',
-				'VEC3',
-				'VEC4'
+				1: 'SCALAR',
+				2: 'VEC2',
+				3: 'VEC3',
+				4: 'VEC4',
+				16: 'MAT4'
 
-			];
+			};
 
 			var componentType;
 
@@ -322,14 +351,25 @@ THREE.GLTFExporter.prototype = {
 			var count = attribute.count;
 
 			// @TODO Indexed buffer geometry with drawRange not supported yet
-			if ( options.truncateDrawRange && geometry.index === null ) {
+			if ( options.truncateDrawRange && geometry !== undefined && geometry.index === null ) {
 
 				start = geometry.drawRange.start;
 				count = geometry.drawRange.count !== Infinity ? geometry.drawRange.count : attribute.count;
 
 			}
 
-			var bufferView = processBufferView( attribute, componentType, start, count );
+			var bufferViewTarget;
+
+			// If geometry isn't provided, don't infer the target usage of the bufferView. For
+			// animation samplers, target must not be set.
+			if ( geometry !== undefined ) {
+
+				var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
+				bufferViewTarget = isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER;
+
+			}
+
+			var bufferView = processBufferView( attribute, componentType, start, count, bufferViewTarget );
 
 			var gltfAccessor = {
 
@@ -339,7 +379,7 @@ THREE.GLTFExporter.prototype = {
 				count: count,
 				max: minMax.max,
 				min: minMax.min,
-				type: types[ attribute.itemSize - 1 ]
+				type: types[ attribute.itemSize ]
 
 			};
 
@@ -368,15 +408,31 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
-			var gltfImage = {};
+			var mimeType = map.format === THREE.RGBAFormat ? 'image/png' : 'image/jpeg';
+			var gltfImage = {mimeType: mimeType};
 
 			if ( options.embedImages ) {
 
-				// @TODO { bufferView, mimeType }
+				var canvas = cachedCanvas = cachedCanvas || document.createElement( 'canvas' );
+				canvas.width = map.image.width;
+				canvas.height = map.image.height;
+				var ctx = canvas.getContext( '2d' );
+
+				if ( map.flipY === true ) {
+
+					ctx.translate( 0, map.image.height );
+					ctx.scale( 1, -1 );
+
+				}
+
+				ctx.drawImage( map.image, 0, 0 );
+
+				// @TODO Embed in { bufferView } if options.binary set.
+
+				gltfImage.uri = canvas.toDataURL( mimeType );
 
 			} else {
 
-				// @TODO base64 based on options
 				gltfImage.uri = map.image.src;
 
 			}
@@ -510,7 +566,7 @@ THREE.GLTFExporter.prototype = {
 
 				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
 
-					index: processTexture( material.map )
+					index: processTexture( material.map )
 
 				};
 
@@ -536,7 +592,7 @@ THREE.GLTFExporter.prototype = {
 
 					gltfMaterial.emissiveTexture = {
 
-						index: processTexture( material.emissiveMap )
+						index: processTexture( material.emissiveMap )
 
 					};
 
@@ -549,7 +605,7 @@ THREE.GLTFExporter.prototype = {
 
 				gltfMaterial.normalTexture = {
 
-					index: processTexture( material.normalMap )
+					index: processTexture( material.normalMap )
 
 				};
 
@@ -572,7 +628,7 @@ THREE.GLTFExporter.prototype = {
 
 				gltfMaterial.occlusionTexture = {
 
-					index: processTexture( material.aoMap )
+					index: processTexture( material.aoMap )
 
 				};
 
@@ -585,11 +641,12 @@ THREE.GLTFExporter.prototype = {
 			}
 
 			// alphaMode
-			if ( material.transparent ) {
+			if ( material.transparent || material.alphaTest > 0.0 ) {
 
-				gltfMaterial.alphaMode = 'MASK'; // @FIXME We should detect MASK or BLEND
+				gltfMaterial.alphaMode = material.opacity < 1.0 ? 'BLEND' : 'MASK';
 
-				if ( material.alphaTest !== 0.5 ) {
+				// Write alphaCutoff if it's non-zero and different from the default (0.5).
+				if ( material.alphaTest > 0.0 && material.alphaTest !== 0.5 ) {
 
 					gltfMaterial.alphaCutoff = material.alphaTest;
 
@@ -723,7 +780,35 @@ THREE.GLTFExporter.prototype = {
 
 				var attribute = geometry.attributes[ attributeName ];
 				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
-				gltfAttributes[ attributeName ] = processAccessor( attribute, geometry );
+
+				if ( attributeName.substr( 0, 5 ) !== 'MORPH' ) {
+
+					gltfAttributes[ attributeName ] = processAccessor( attribute, geometry );
+
+				}
+
+			}
+
+			// Morph targets
+			if ( mesh.morphTargetInfluences !== undefined && mesh.morphTargetInfluences.length > 0 ) {
+
+				gltfMesh.primitives[ 0 ].targets = [];
+
+				for ( var i = 0; i < mesh.morphTargetInfluences.length; ++ i ) {
+
+					var target = {};
+
+					for ( var attributeName in geometry.morphAttributes ) {
+
+						var attribute = geometry.morphAttributes[ attributeName ][ i ];
+						attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
+						target[ attributeName ] = processAccessor( attribute, geometry );
+
+					}
+
+					gltfMesh.primitives[ 0 ].targets.push( target );
+
+				}
 
 			}
 
@@ -790,6 +875,123 @@ THREE.GLTFExporter.prototype = {
 
 		}
 
+		/**
+		 * Creates glTF animation entry from AnimationClip object.
+		 *
+		 * Status:
+		 * - Only properties listed in PATH_PROPERTIES may be animated.
+		 * - Only LINEAR and STEP interpolation currently supported.
+		 *
+		 * @param {THREE.AnimationClip} clip
+		 * @param {THREE.Object3D} root
+		 * @return {number}
+		 */
+		function processAnimation ( clip, root ) {
+
+			if ( ! outputJSON.animations ) {
+
+				outputJSON.animations = [];
+
+			}
+
+			var channels = [];
+			var samplers = [];
+
+			for ( var i = 0; i < clip.tracks.length; ++ i ) {
+
+				var track = clip.tracks[ i ];
+				var trackBinding = THREE.PropertyBinding.parseTrackName( track.name );
+				var trackNode = THREE.PropertyBinding.findNode( root, trackBinding.nodeName );
+				var trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ];
+
+				if ( ! trackNode || ! trackProperty ) {
+
+					console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
+					return null;
+
+				}
+
+				var inputItemSize = 1;
+				var outputItemSize = track.values.length / track.times.length;
+
+				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {
+
+					outputItemSize /= trackNode.morphTargetInfluences.length;
+
+				}
+
+				samplers.push( {
+
+					input: processAccessor( new THREE.BufferAttribute( track.times, inputItemSize ) ),
+					output: processAccessor( new THREE.BufferAttribute( track.values, outputItemSize ) ),
+					interpolation: track.interpolation === THREE.InterpolateDiscrete ? 'STEP' : 'LINEAR'
+
+				} );
+
+				channels.push( {
+
+					sampler: samplers.length - 1,
+					target: {
+						node: nodeMap[ trackNode.uuid ],
+						path: trackProperty
+					}
+
+				} );
+
+			}
+
+			outputJSON.animations.push( {
+
+				name: clip.name || 'clip_' + outputJSON.animations.length,
+				samplers: samplers,
+				channels: channels
+
+			} );
+
+			return outputJSON.animations.length - 1;
+
+		}
+
+		function processSkin( object ) {
+
+			var node = outputJSON.nodes[ nodeMap[ object.uuid ] ];
+
+			var skeleton = object.skeleton;
+			var rootJoint = object.skeleton.bones[ 0 ];
+
+			if ( rootJoint === undefined ) return null;
+
+			var joints = [];
+			var inverseBindMatrices = new Float32Array( skeleton.bones.length * 16 );
+
+			for ( var i = 0; i < skeleton.bones.length; ++ i ) {
+
+				joints.push( nodeMap[ skeleton.bones[ i ].uuid ] );
+
+				skeleton.boneInverses[ i ].toArray( inverseBindMatrices, i * 16 );
+
+			}
+
+			if ( outputJSON.skins === undefined ) {
+
+				outputJSON.skins = [];
+
+			}
+
+			outputJSON.skins.push( {
+
+				inverseBindMatrices: processAccessor( new THREE.BufferAttribute( inverseBindMatrices, 16 ) ),
+				joints: joints,
+				skeleton: nodeMap[ rootJoint.uuid ]
+
+			} );
+
+			var skinIndex = node.skin = outputJSON.skins.length - 1;
+
+			return skinIndex;
+
+		}
+
 		/**
 		 * Process Object3D node
 		 * @param  {THREE.Object3D} node Object3D to processNode
@@ -826,7 +1028,7 @@ THREE.GLTFExporter.prototype = {
 
 				if ( ! equalArray( position, [ 0, 0, 0 ] ) ) {
 
-					gltfNode.position = position;
+					gltfNode.translation = position;
 
 				}
 
@@ -879,6 +1081,12 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
+			if ( object instanceof THREE.SkinnedMesh ) {
+
+				skins.push( object );
+
+			}
+
 			if ( object.children.length > 0 ) {
 
 				var children = [];
@@ -912,7 +1120,9 @@ THREE.GLTFExporter.prototype = {
 
 			outputJSON.nodes.push( gltfNode );
 
-			return outputJSON.nodes.length - 1;
+			var nodeIndex = nodeMap[ object.uuid ] = outputJSON.nodes.length - 1;
+
+			return nodeIndex;
 
 		}
 
@@ -1018,6 +1228,18 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
+			for ( var i = 0; i < skins.length; ++ i ) {
+
+				processSkin( skins[ i ] );
+
+			}
+
+			for ( var i = 0; i < options.animations.length; ++ i ) {
+
+				processAnimation( options.animations[ i ], input[ 0 ] );
+
+			}
+
 		}
 
 		processInput( input );

+ 0 - 654
examples/js/libs/earcut.js

@@ -1,654 +0,0 @@
-/**
- *
- * Earcut https://github.com/mapbox/earcut
- *
- * Copyright (c) 2016, Mapbox
- *
- * Permission to use, copy, modify, and/or distribute this software for any purpose
- * with or without fee is hereby granted, provided that the above copyright notice
- * and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
- * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- */
-'use strict';
-
-//module.exports = earcut;
-
-function earcut(data, holeIndices, dim) {
-
-    dim = dim || 2;
-
-    var hasHoles = holeIndices && holeIndices.length,
-        outerLen = hasHoles ? holeIndices[0] * dim : data.length,
-        outerNode = linkedList(data, 0, outerLen, dim, true),
-        triangles = [];
-
-    if (!outerNode) return triangles;
-
-    var minX, minY, maxX, maxY, x, y, size;
-
-    if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
-
-    // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
-    if (data.length > 80 * dim) {
-        minX = maxX = data[0];
-        minY = maxY = data[1];
-
-        for (var i = dim; i < outerLen; i += dim) {
-            x = data[i];
-            y = data[i + 1];
-            if (x < minX) minX = x;
-            if (y < minY) minY = y;
-            if (x > maxX) maxX = x;
-            if (y > maxY) maxY = y;
-        }
-
-        // minX, minY and size are later used to transform coords into integers for z-order calculation
-        size = Math.max(maxX - minX, maxY - minY);
-    }
-
-    earcutLinked(outerNode, triangles, dim, minX, minY, size);
-
-    return triangles;
-}
-
-// create a circular doubly linked list from polygon points in the specified winding order
-function linkedList(data, start, end, dim, clockwise) {
-    var i, last;
-
-    if (clockwise === (signedArea(data, start, end, dim) > 0)) {
-        for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
-    } else {
-        for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
-    }
-
-    if (last && equals(last, last.next)) {
-        removeNode(last);
-        last = last.next;
-    }
-
-    return last;
-}
-
-// eliminate colinear or duplicate points
-function filterPoints(start, end) {
-    if (!start) return start;
-    if (!end) end = start;
-
-    var p = start,
-        again;
-    do {
-        again = false;
-
-        if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
-            removeNode(p);
-            p = end = p.prev;
-            if (p === p.next) return null;
-            again = true;
-
-        } else {
-            p = p.next;
-        }
-    } while (again || p !== end);
-
-    return end;
-}
-
-// main ear slicing loop which triangulates a polygon (given as a linked list)
-function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
-    if (!ear) return;
-
-    // interlink polygon nodes in z-order
-    if (!pass && size) indexCurve(ear, minX, minY, size);
-
-    var stop = ear,
-        prev, next;
-
-    // iterate through ears, slicing them one by one
-    while (ear.prev !== ear.next) {
-        prev = ear.prev;
-        next = ear.next;
-
-        if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) {
-            // cut off the triangle
-            triangles.push(prev.i / dim);
-            triangles.push(ear.i / dim);
-            triangles.push(next.i / dim);
-
-            removeNode(ear);
-
-            // skipping the next vertice leads to less sliver triangles
-            ear = next.next;
-            stop = next.next;
-
-            continue;
-        }
-
-        ear = next;
-
-        // if we looped through the whole remaining polygon and can't find any more ears
-        if (ear === stop) {
-            // try filtering points and slicing again
-            if (!pass) {
-                earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1);
-
-            // if this didn't work, try curing all small self-intersections locally
-            } else if (pass === 1) {
-                ear = cureLocalIntersections(ear, triangles, dim);
-                earcutLinked(ear, triangles, dim, minX, minY, size, 2);
-
-            // as a last resort, try splitting the remaining polygon into two
-            } else if (pass === 2) {
-                splitEarcut(ear, triangles, dim, minX, minY, size);
-            }
-
-            break;
-        }
-    }
-}
-
-// check whether a polygon node forms a valid ear with adjacent nodes
-function isEar(ear) {
-    var a = ear.prev,
-        b = ear,
-        c = ear.next;
-
-    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
-
-    // now make sure we don't have other points inside the potential ear
-    var p = ear.next.next;
-
-    while (p !== ear.prev) {
-        if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
-            area(p.prev, p, p.next) >= 0) return false;
-        p = p.next;
-    }
-
-    return true;
-}
-
-function isEarHashed(ear, minX, minY, size) {
-    var a = ear.prev,
-        b = ear,
-        c = ear.next;
-
-    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
-
-    // triangle bbox; min & max are calculated like this for speed
-    var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x),
-        minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y),
-        maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x),
-        maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
-
-    // z-order range for the current triangle bbox;
-    var minZ = zOrder(minTX, minTY, minX, minY, size),
-        maxZ = zOrder(maxTX, maxTY, minX, minY, size);
-
-    // first look for points inside the triangle in increasing z-order
-    var p = ear.nextZ;
-
-    while (p && p.z <= maxZ) {
-        if (p !== ear.prev && p !== ear.next &&
-            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
-            area(p.prev, p, p.next) >= 0) return false;
-        p = p.nextZ;
-    }
-
-    // then look for points in decreasing z-order
-    p = ear.prevZ;
-
-    while (p && p.z >= minZ) {
-        if (p !== ear.prev && p !== ear.next &&
-            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
-            area(p.prev, p, p.next) >= 0) return false;
-        p = p.prevZ;
-    }
-
-    return true;
-}
-
-// go through all polygon nodes and cure small local self-intersections
-function cureLocalIntersections(start, triangles, dim) {
-    var p = start;
-    do {
-        var a = p.prev,
-            b = p.next.next;
-
-        if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
-
-            triangles.push(a.i / dim);
-            triangles.push(p.i / dim);
-            triangles.push(b.i / dim);
-
-            // remove two nodes involved
-            removeNode(p);
-            removeNode(p.next);
-
-            p = start = b;
-        }
-        p = p.next;
-    } while (p !== start);
-
-    return p;
-}
-
-// try splitting polygon into two and triangulate them independently
-function splitEarcut(start, triangles, dim, minX, minY, size) {
-    // look for a valid diagonal that divides the polygon into two
-    var a = start;
-    do {
-        var b = a.next.next;
-        while (b !== a.prev) {
-            if (a.i !== b.i && isValidDiagonal(a, b)) {
-                // split the polygon in two by the diagonal
-                var c = splitPolygon(a, b);
-
-                // filter colinear points around the cuts
-                a = filterPoints(a, a.next);
-                c = filterPoints(c, c.next);
-
-                // run earcut on each half
-                earcutLinked(a, triangles, dim, minX, minY, size);
-                earcutLinked(c, triangles, dim, minX, minY, size);
-                return;
-            }
-            b = b.next;
-        }
-        a = a.next;
-    } while (a !== start);
-}
-
-// link every hole into the outer loop, producing a single-ring polygon without holes
-function eliminateHoles(data, holeIndices, outerNode, dim) {
-    var queue = [],
-        i, len, start, end, list;
-
-    for (i = 0, len = holeIndices.length; i < len; i++) {
-        start = holeIndices[i] * dim;
-        end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
-        list = linkedList(data, start, end, dim, false);
-        if (list === list.next) list.steiner = true;
-        queue.push(getLeftmost(list));
-    }
-
-    queue.sort(compareX);
-
-    // process holes from left to right
-    for (i = 0; i < queue.length; i++) {
-        eliminateHole(queue[i], outerNode);
-        outerNode = filterPoints(outerNode, outerNode.next);
-    }
-
-    return outerNode;
-}
-
-function compareX(a, b) {
-    return a.x - b.x;
-}
-
-// find a bridge between vertices that connects hole with an outer ring and and link it
-function eliminateHole(hole, outerNode) {
-    outerNode = findHoleBridge(hole, outerNode);
-    if (outerNode) {
-        var b = splitPolygon(outerNode, hole);
-        filterPoints(b, b.next);
-    }
-}
-
-// David Eberly's algorithm for finding a bridge between hole and outer polygon
-function findHoleBridge(hole, outerNode) {
-    var p = outerNode,
-        hx = hole.x,
-        hy = hole.y,
-        qx = -Infinity,
-        m;
-
-    // find a segment intersected by a ray from the hole's leftmost point to the left;
-    // segment's endpoint with lesser x will be potential connection point
-    do {
-        if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
-            var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
-            if (x <= hx && x > qx) {
-                qx = x;
-                if (x === hx) {
-                    if (hy === p.y) return p;
-                    if (hy === p.next.y) return p.next;
-                }
-                m = p.x < p.next.x ? p : p.next;
-            }
-        }
-        p = p.next;
-    } while (p !== outerNode);
-
-    if (!m) return null;
-
-    if (hx === qx) return m.prev; // hole touches outer segment; pick lower endpoint
-
-    // look for points inside the triangle of hole point, segment intersection and endpoint;
-    // if there are no points found, we have a valid connection;
-    // otherwise choose the point of the minimum angle with the ray as connection point
-
-    var stop = m,
-        mx = m.x,
-        my = m.y,
-        tanMin = Infinity,
-        tan;
-
-    p = m.next;
-
-    while (p !== stop) {
-        if (hx >= p.x && p.x >= mx && hx !== p.x &&
-                pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
-
-            tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
-
-            if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) {
-                m = p;
-                tanMin = tan;
-            }
-        }
-
-        p = p.next;
-    }
-
-    return m;
-}
-
-// interlink polygon nodes in z-order
-function indexCurve(start, minX, minY, size) {
-    var p = start;
-    do {
-        if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size);
-        p.prevZ = p.prev;
-        p.nextZ = p.next;
-        p = p.next;
-    } while (p !== start);
-
-    p.prevZ.nextZ = null;
-    p.prevZ = null;
-
-    sortLinked(p);
-}
-
-// Simon Tatham's linked list merge sort algorithm
-// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
-function sortLinked(list) {
-    var i, p, q, e, tail, numMerges, pSize, qSize,
-        inSize = 1;
-
-    do {
-        p = list;
-        list = null;
-        tail = null;
-        numMerges = 0;
-
-        while (p) {
-            numMerges++;
-            q = p;
-            pSize = 0;
-            for (i = 0; i < inSize; i++) {
-                pSize++;
-                q = q.nextZ;
-                if (!q) break;
-            }
-            qSize = inSize;
-
-            while (pSize > 0 || (qSize > 0 && q)) {
-
-                if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
-                    e = p;
-                    p = p.nextZ;
-                    pSize--;
-                } else {
-                    e = q;
-                    q = q.nextZ;
-                    qSize--;
-                }
-
-                if (tail) tail.nextZ = e;
-                else list = e;
-
-                e.prevZ = tail;
-                tail = e;
-            }
-
-            p = q;
-        }
-
-        tail.nextZ = null;
-        inSize *= 2;
-
-    } while (numMerges > 1);
-
-    return list;
-}
-
-// z-order of a point given coords and size of the data bounding box
-function zOrder(x, y, minX, minY, size) {
-    // coords are transformed into non-negative 15-bit integer range
-    x = 32767 * (x - minX) / size;
-    y = 32767 * (y - minY) / size;
-
-    x = (x | (x << 8)) & 0x00FF00FF;
-    x = (x | (x << 4)) & 0x0F0F0F0F;
-    x = (x | (x << 2)) & 0x33333333;
-    x = (x | (x << 1)) & 0x55555555;
-
-    y = (y | (y << 8)) & 0x00FF00FF;
-    y = (y | (y << 4)) & 0x0F0F0F0F;
-    y = (y | (y << 2)) & 0x33333333;
-    y = (y | (y << 1)) & 0x55555555;
-
-    return x | (y << 1);
-}
-
-// find the leftmost node of a polygon ring
-function getLeftmost(start) {
-    var p = start,
-        leftmost = start;
-    do {
-        if (p.x < leftmost.x) leftmost = p;
-        p = p.next;
-    } while (p !== start);
-
-    return leftmost;
-}
-
-// check if a point lies within a convex triangle
-function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
-    return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
-           (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
-           (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
-}
-
-// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
-function isValidDiagonal(a, b) {
-    return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) &&
-           locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
-}
-
-// signed area of a triangle
-function area(p, q, r) {
-    return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
-}
-
-// check if two points are equal
-function equals(p1, p2) {
-    return p1.x === p2.x && p1.y === p2.y;
-}
-
-// check if two segments intersect
-function intersects(p1, q1, p2, q2) {
-    if ((equals(p1, q1) && equals(p2, q2)) ||
-        (equals(p1, q2) && equals(p2, q1))) return true;
-    return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 &&
-           area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
-}
-
-// check if a polygon diagonal intersects any polygon segments
-function intersectsPolygon(a, b) {
-    var p = a;
-    do {
-        if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
-                intersects(p, p.next, a, b)) return true;
-        p = p.next;
-    } while (p !== a);
-
-    return false;
-}
-
-// check if a polygon diagonal is locally inside the polygon
-function locallyInside(a, b) {
-    return area(a.prev, a, a.next) < 0 ?
-        area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
-        area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
-}
-
-// check if the middle point of a polygon diagonal is inside the polygon
-function middleInside(a, b) {
-    var p = a,
-        inside = false,
-        px = (a.x + b.x) / 2,
-        py = (a.y + b.y) / 2;
-    do {
-        if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
-                (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
-            inside = !inside;
-        p = p.next;
-    } while (p !== a);
-
-    return inside;
-}
-
-// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
-// if one belongs to the outer ring and another to a hole, it merges it into a single ring
-function splitPolygon(a, b) {
-    var a2 = new Node(a.i, a.x, a.y),
-        b2 = new Node(b.i, b.x, b.y),
-        an = a.next,
-        bp = b.prev;
-
-    a.next = b;
-    b.prev = a;
-
-    a2.next = an;
-    an.prev = a2;
-
-    b2.next = a2;
-    a2.prev = b2;
-
-    bp.next = b2;
-    b2.prev = bp;
-
-    return b2;
-}
-
-// create a node and optionally link it with previous one (in a circular doubly linked list)
-function insertNode(i, x, y, last) {
-    var p = new Node(i, x, y);
-
-    if (!last) {
-        p.prev = p;
-        p.next = p;
-
-    } else {
-        p.next = last.next;
-        p.prev = last;
-        last.next.prev = p;
-        last.next = p;
-    }
-    return p;
-}
-
-function removeNode(p) {
-    p.next.prev = p.prev;
-    p.prev.next = p.next;
-
-    if (p.prevZ) p.prevZ.nextZ = p.nextZ;
-    if (p.nextZ) p.nextZ.prevZ = p.prevZ;
-}
-
-function Node(i, x, y) {
-    // vertice index in coordinates array
-    this.i = i;
-
-    // vertex coordinates
-    this.x = x;
-    this.y = y;
-
-    // previous and next vertice nodes in a polygon ring
-    this.prev = null;
-    this.next = null;
-
-    // z-order curve value
-    this.z = null;
-
-    // previous and next nodes in z-order
-    this.prevZ = null;
-    this.nextZ = null;
-
-    // indicates whether this is a steiner point
-    this.steiner = false;
-}
-
-// return a percentage difference between the polygon area and its triangulation area;
-// used to verify correctness of triangulation
-earcut.deviation = function (data, holeIndices, dim, triangles) {
-    var hasHoles = holeIndices && holeIndices.length;
-    var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
-
-    var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
-    if (hasHoles) {
-        for (var i = 0, len = holeIndices.length; i < len; i++) {
-            var start = holeIndices[i] * dim;
-            var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
-            polygonArea -= Math.abs(signedArea(data, start, end, dim));
-        }
-    }
-
-    var trianglesArea = 0;
-    for (i = 0; i < triangles.length; i += 3) {
-        var a = triangles[i] * dim;
-        var b = triangles[i + 1] * dim;
-        var c = triangles[i + 2] * dim;
-        trianglesArea += Math.abs(
-            (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
-            (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
-    }
-
-    return polygonArea === 0 && trianglesArea === 0 ? 0 :
-        Math.abs((trianglesArea - polygonArea) / polygonArea);
-};
-
-function signedArea(data, start, end, dim) {
-    var sum = 0;
-    for (var i = start, j = end - dim; i < end; i += dim) {
-        sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
-        j = i;
-    }
-    return sum;
-}
-
-// turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
-earcut.flatten = function (data) {
-    var dim = data[0][0].length,
-        result = {vertices: [], holes: [], dimensions: dim},
-        holeIndex = 0;
-
-    for (var i = 0; i < data.length; i++) {
-        for (var j = 0; j < data[i].length; j++) {
-            for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]);
-        }
-        if (i > 0) {
-            holeIndex += data[i - 1].length;
-            result.holes.push(holeIndex);
-        }
-    }
-    return result;
-};

+ 0 - 19
examples/js/libs/pnltri.min.js

@@ -1,19 +0,0 @@
-// pnltri.js / raw.github.com/jahting/pnltri.js/master/LICENSE
-'use strict';var r={ca:"2.0"};r.c={random:Math.random,da:function(b){for(var c=b.length-1;0<c;c--){var l=Math.floor(r.c.random()*(c+1)),k=b[c];b[c]=b[l];b[l]=k}return b},G:function(b,c){var l=b.y-c.y;if(l<r.c.D)return-1;if(l>r.c.n)return 1;l=b.x-c.x;return l<r.c.D?-1:l>r.c.n?1:0},N:function(b,c,l){return(c.x-b.x)*(l.y-b.y)-(c.y-b.y)*(l.x-b.x)}};r.c.n=Math.pow(2,-43);r.c.D=-r.c.n;function u(b){this.U=[];this.r=[];this.X=[];this.L=0;this.F=[];this.R=[];this.T=[];if(b)for(var c=0,l=b.length;c<l;c++){var k=w(this,b[c]);if(3>k.length)console.log("Polygon has < 3 vertices!",k);else{for(var a=void 0,e=void 0,d=void 0,f=0;f<k.length-1;f++)a=x(this,k[f],k[f+1]),d?(a.o=d,d.m=a):e=a,d=a,this.r.push(a);a=x(this,k[k.length-1],k[0]);a.o=d;d.m=a;this.r.push(a);e.o=a;a.m=e;this.F[this.L++]=!0}}}u.prototype={Q:function(){return this.F.concat()}};
-function B(b,c,l,k){b.T.push([c.id,l.id,k.id])}
-function w(b,c){function l(a,b){return Math.abs(a.x-b.x)<r.c.n&&Math.abs(a.y-b.y)<r.c.n}function k(a,b,c){if(Math.abs(r.c.N(b,a,c))>r.c.n)return!1;var d;Math.abs(a.y-b.y)<r.c.n?(d=b.x,a.x<c.x?(b=a.x,a=c.x):(b=c.x,a=a.x)):(d=b.y,a.y<c.y?(b=a.y,a=c.y):(b=c.y,a=a.y));return b-d<r.c.n&&d-a<r.c.n}for(var a=[],e,d,f,h=0;h<c.length;h++)e=D(b,c[h].x,c[h].y),d=!0,f=a.length-1,0<=f&&(l(e,a[f])?d=!1:0<f&&k(a[f-1],a[f],e)&&a.pop()),d&&a.push(e);f=a.length-1;0<f&&l(a[f],a[0])&&(a.pop(),f--);1<f&&(k(a[f-1],a[f],
-a[0])&&(a.pop(),f--),1<f&&k(a[f],a[0],a[1])&&a.shift());return a}function x(b,c,l){return{O:b.L,a:c,i:l,p:1==r.c.G(l,c),o:null,m:null,H:null,I:null,v:!1,J:null,K:null,j:null,g:null,w:!1}}function D(b,c,l){c={id:b.U.length,x:c,y:l};b.U.push(c);return c};function E(b){this.k=b}E.prototype={};function F(b,c,l,k){this.q=b?b:{x:Number.POSITIVE_INFINITY,y:Number.POSITIVE_INFINITY};this.l=c?c:{x:Number.NEGATIVE_INFINITY,y:Number.NEGATIVE_INFINITY};this.t=l;this.s=k;this.depth=-1}F.prototype={};function G(b){var c=new F(b.q,b.l,b.t,b.s);c.e=b.e;c.f=b.f;c.b=b.b;c.d=b.d;c.u=b.u;return c}function H(b){this.A=b;b.u=this}H.prototype={};function I(b){var c=new F(null,null,null,null);this.B=[];J(this,c);this.root=new H(c);if(b)for(b=b.r,c=0;c<b.length;c++)b[c].H=b[c].I=this.root,b[c].v=!1}
-I.prototype={P:function(b){var c,l,k=b.U.length,a=Array(k);for(c=0;c<k;c++)a[c]=Array(8);var e=Array(k);c=0;for(l=this.B.length;c<l;c++){var d=this.B[c],f=d.e?d.f?5:7:d.f?4:6,h=d.b?d.d?1:0:d.d?3:2;if(1==d.depth%2){if(5==f||1==h||7==f&&3==h||4==f&&0==h){var g;g={a:d.l,i:d.q,j:null,g:null,w:!1};b.X.push(g);var p;p={a:d.q,i:d.l,aa:g,j:null,g:null,w:!1};b.X.push(p);g.aa=p;a[d.l.id][h]=g;a[d.q.id][f]=p}}else null!=d.q.id&&(e[d.q.id]=f),null!=d.l.id&&(e[d.l.id]=h)}var t;for(c=0;c<k;c++)if(d=a[c],f=e[c],
-null!=f){l=f;h=null;do if(7<l++&&(l=0),b=d[l])h?(b.j=h,h.g=b):(t=b.a,t.Y=b),h=b.aa;while(l!=f);h&&(t.Z=h)}}};
-function L(b,c){function l(){var a=m.e||m.f;a.b&&a.d?m==a.b?(n.e=null,a.b=q):(q.f=null,a.d=n):(n.e=null,n.f=a,a.d=n)}function k(a){m.l==t.l?(f?m.b?(a.e=q,q.b=a,n.d=null):(a.f=n,q.b=null,n.d=a):(a.e=q,a.f=n,q.b=n.d=a),q.d=n.b=null):(a.e&&a.f&&(a.e==m?(a.C=a.f,a.ba=!0):(a.C=a.e,a.ba=!1)),a.e=q,a.f=n,q.d=n.b=a,q.b=n.d=null)}function a(){var a;if(m.l==t.l&&f)m.b.e=q,m.d.f=n,q.b=m.b,n.d=m.d,a=q.d=n.b=null;else{m.b.e=q;m.d.f=n;var b=M(c,m.l);if(0<b)a=!0;else if(0>b)a=!1;else{a=m.b.s;var d=a.p,b=d?a.a:a.i,
-b=M(c,b);0<b?a=!0:0>b?a=!1:(a=d?a.m:a.o,b=d?a.i:a.a,b=M(c,b),a=0<b?!0:!1)}a?(a=m.d,m.d.e=q,q.b=m.b,n.d=null):(a=m.b,m.b.f=n,n.d=m.d,q.b=null);q.d=n.b=a}return a}N(c);var e,d,f,h,g,p;c.p?(e=c.a,h=c.i,d=c.H,g=c.I,f=c.o.v,p=c.m.v):(e=c.i,h=c.a,d=c.I,g=c.H,f=c.m.v,p=c.o.v);p||(p=O(b,g,h,!1),g==d&&(d=p),g=p);g=g.A;if(g.e||g.f)if(g.q!=h)console.log("ERR add_segment: trFirstHigh != segHigh: ",g);else{f||(d=O(b,d,e,!0));var t=d.A,m=g,q,n,y,v;for(e=b.B.length+2;m;){if(0>--e){console.log("ERR add_segment: infinite loop",
-m,c,b);return}if(!m.b&&!m.d){console.log("ERR add_segment: missing successors",m,c,b);return}d=m.u;d.h=c;d.A=null;v&&v.s==m.s?(q=m,n=v,n.l=m.l,d.left=new H(q),d.right=v.u):(y&&y.t==m.t?(n=m,q=y,q.l=m.l,d.left=y.u):(q=m,n=P(b,m),d.left=new H(q)),d.right=new H(n));m.e&&m.f?m.C?(m.ba?(n.e=m.f,n.f=m.C,n.e.b=n,n.f.d=n):(q.f=m.e,q.e=m.C,q.e.b=q,q.f.d=q),q.C=n.C=null):m.q==g.q?(n.f.d=n,q.f=n.e=null):n==m?(n.e=n.f,n.f=null,n.e.b=n):(q.f=q.e,q.e=null):l();m.b&&m.d?d=a():(d=m.b?m.b:m.d,k(d));q.s&&(q.s.J=n);
-n.t&&(n.t.K=q);q.s=n.t=c;c.J=q;c.K=n;m.l!=t.l?(y=q,v=n,m=d):m=null}c.v=!0}else console.log("ERR add_segment: missing trFirst.uX: ",g)}
-function Q(b,c){if(c)var l=b.a,k=b.i,a=b.H;else l=b.i,k=b.a,a=b.I;for(var e;a;)if(a.V)a=-1==r.c.G(l==a.V?k:l,a.V)?a.left:a.right;else if(a.h){if(l==a.h.a||l==a.h.i)if(Math.abs(l.y-k.y)<r.c.n){a=Math.abs(a.h.a.y-a.h.i.y)<r.c.n?l==a.h.a?((e=b.p?k.x>=a.h.i.x:k.x<a.h.i.x)?b.o.p:a.h.m.p)?a.right:a.left:((e=b.p?k.x<a.h.a.x:k.x>=a.h.a.x)?b.m.p:a.h.o.p)?a.left:a.right:k.x<l.x?a.left:a.right;continue}else e=M(a.h,k),0==e&&(e=l==a.h.a?(e=b.p?k.y>=a.h.i.y:k.y<a.h.i.y)?M(a.h,b.o.a):-M(a.h,a.h.m.i):(e=b.p?k.y<
-a.h.a.y:k.y>=a.h.a.y)?M(a.h,b.m.i):-M(a.h,a.h.o.a));else e=M(a.h,l),0==e&&(e=M(a.h,k),0==e&&(e=M(a.h,c?b.o.a:b.m.i)));if(0<e)a=a.left;else if(0>e)a=a.right;else break}else{a.A||console.log("ptNode: unknown type",a);c?b.H=a:b.I=a;break}}function N(b){Q(b,!0);Q(b,!1)}function M(b,c){var l;l=b.a.x-c.x;var k=b.i.x-c.x,a=Math.abs(b.a.y-c.y)<r.c.n;if(Math.abs(b.i.y-c.y)<r.c.n){if(a)return 0;l=k}else if(!a)return b.p?r.c.N(b.a,b.i,c):r.c.N(b.i,b.a,c);return Math.abs(l)<r.c.n?0:l}
-function O(b,c,l,k){var a=c.A;if(a.q==l||a.l==l)return c;var e=G(a);a.l=e.q=l;a.b=e;e.e=a;a.d=e.f=null;e.b&&(e.b.e=e);e.d&&(e.d.f=e);J(b,e);c.V=l;c.A=null;c.right=new H(a);c.left=new H(e);return k?a.u:e.u}function P(b,c){var l=G(c);J(b,l);return l}function J(b,c){c.fa=b.B.length;b.B.push(c)}function V(b){this.k=b;this.$=new I(this.k)}V.prototype={P:function(){return this.$.P(this.k)}};function W(b){this.k=b;this.S=null}W.prototype={};function X(b){this.k=b}X.prototype={};function Z(){this.M=null}
-Z.prototype={W:function(){this.M=null},Q:function(){return this.M?this.M.Q():null},ea:function(b,c){this.W();if(!b||0==b.length)return[];var l=new u(b),k=c?!1:1==l.L;if(k){k=new E(l);a:{var a=k.k,e=a.r[0],d=e,f=e,h=0;do h+=(f.a.x-f.i.x)*(f.a.y+f.i.y),f=f.m;while(f!=e);if(0>h){do d.j=d.m,d=d.g=d.o;while(d!=e);a.F[0]=!1}else{do d.j=d.o,d=d.g=d.m;while(d!=e)}for(e=a=e;a.g!=a.j;){b:{var d=a.j.a.x,f=a.j.a.y,h=a.a.x,g=a.a.y,p=a.g.a.x,t=a.g.a.y,m=p-h,q=t-g,n=d-p,y=f-t,v=h-d,K=g-f;if(r.c.n>v*q-m*K)d=!1;else{for(var Y=
-a.j.j,C=a.g;C!=Y;){var C=C.g,z=C.a.x,A=C.a.y,R=z-d,S=A-f;if(0!=R||0!=S){var T=z-h,U=A-g;if(0!=T||0!=U)if(z-=p,A-=t,(0!=z||0!=A)&&m*U-q*T>=r.c.D&&v*S-K*R>=r.c.D&&n*A-y*z>=r.c.D){d=!1;break b}}}d=!0}}if(d)B(k.k,a.j.a,a.a,a.g.a),a.j.g=a.g,a.g.j=a.j,e=a=a.g;else if(a=a.g,a==e){k=!1;break a}}k=!0}}if(!k){k=new W(l);k.S=new V(k.k);e=k.S;a=e.k.r.concat();r.c.da(a);d=0;f=e.k.L;if(1!=f)for(h=Array(f),g=a.concat(),p=0;p<g.length;p++)t=g[p].O,h[t]?a[f++]=g[p]:(a[d++]=g[p],h[t]=!0);d=a.length;f=e.$;h=0;for(g=
-d;h<d;){g=Math.log(g)/Math.LN2;for(p=1<g?Math.floor(d/g):d;h<p;h++)L(f,a[h]);for(p=h;p<d;p++)N(a[p])}var e=e.k,f=[f.B[0]],h=[],s,p=0;do{for(t=1==p%2;g=f.pop();)-1==g.depth&&(g.depth=p,g.e&&f.push(g.e),g.f&&f.push(g.f),g.b&&f.push(g.b),g.d&&f.push(g.d),(s=g.t)&&-1==s.J.depth&&h.push(s.J),s=g.s)&&(-1==s.K.depth&&h.push(s.K),s.p!=t&&(e.F[s.O]=!1));f=h;h=[];p++}while(0<f.length);for(p=0;p<d;p++)a[p].J=a[p].K=null;k.S.P();s=k.k;d=0;for(f=s.r.length;d<f;d++){a=s.r[d];s.F[a.O]?(e=a.i,a.j=a.o,a.g=a.m):(e=
-a.a,a=a.m,a.j=a.m,a.g=a.o);if(h=a.a.Z)h.g=a,a.j=h,a.a.Z=null;if(h=e.Y)h.j=a,a.g=h,e.Y=null}s=k.k;s.R=[];k=0;for(a=s.r.length;k<a;k++)if(e=s.r[k],!e.w){a:{h=f=d=void 0;g=e;f=h=e.a;e.w=!0;for(e=e.g;d=e.a;){if(e.w){if(d==f)break;console.log("ERR unique_monotone: segment in two chains",f,e);e=null;break a}e.w=!0;1==r.c.G(d,h)&&(h=d,g=e);e=e.g}e=g}e&&s.R.push(e)}s=k=new X(l);k=s.k.R;s.k.T=[];for(a=0;a<k.length;a++)if(f=k[a],e=f.j,d=f.g,d.g==e)B(s.k,f.a,d.a,e.a);else if(e=s,d=f.g,f=f.a,h=[d.a],g=0,d=d.g,
-p=d.a,p!=f){for(;p!=f||1<g;)if(0<g)if(t=r.c.N(h[g],p,h[g-1]),Math.abs(t)<=r.c.n&&(p==f||r.c.G(h[g],p)==r.c.G(h[g],h[g-1]))&&(t=1),0<t)B(e.k,h[g-1],h[g],p),g--;else if(h[++g]=p,p==f)for(console.log("ERR uni-y-monotone: only concave angles left",h);1<g;)g--,B(e.k,h[g-1],h[g],h[g+1]);else d=d.g,p=d.a;else h[++g]=p,d=d.g,p=d.a;B(e.k,h[g-1],h[g],p)}}this.M=l;return l.T.concat()}};window.PNLTRI=r;r.REVISION=r.ca;r.Math=r.c;r.Triangulator=Z;Z.prototype.clear_lastData=Z.prototype.W;Z.prototype.get_PolyLeftArr=Z.prototype.Q;Z.prototype.triangulate_polygon=Z.prototype.ea;

+ 4 - 11
examples/js/loaders/3MFLoader.js

@@ -88,23 +88,16 @@ THREE.ThreeMFLoader.prototype = {
 
 			}
 
-			if ( window.TextDecoder === undefined ) {
-
-				console.error( 'THREE.ThreeMFLoader: TextDecoder not present. Please use a TextDecoder polyfill.' );
-				return null;
-
-			}
-
-			var relsView = new DataView( zip.file( relsName ).asArrayBuffer() );
-			var relsFileText = new TextDecoder( 'utf-8' ).decode( relsView );
+			var relsView = new Uint8Array( zip.file( relsName ).asArrayBuffer() );
+			var relsFileText = THREE.LoaderUtils.decodeText( relsView );
 			rels = parseRelsXml( relsFileText );
 
 			for ( var i = 0; i < modelPartNames.length; i ++ ) {
 
 				var modelPart = modelPartNames[ i ];
-				var view = new DataView( zip.file( modelPart ).asArrayBuffer() );
+				var view = new Uint8Array( zip.file( modelPart ).asArrayBuffer() );
 
-				var fileText = new TextDecoder( 'utf-8' ).decode( view );
+				var fileText = THREE.LoaderUtils.decodeText( view );
 				var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
 
 				if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {

+ 1 - 9
examples/js/loaders/AMFLoader.js

@@ -14,7 +14,6 @@
  *
  * Materials now supported, material colors supported
  * Zip support, requires jszip
- * TextDecoder polyfill required by some browsers (particularly IE, Edge)
  * No constellation support (yet)!
  *
  */
@@ -87,14 +86,7 @@ THREE.AMFLoader.prototype = {
 
 			}
 
-			if ( window.TextDecoder === undefined ) {
-
-				console.log( 'THREE.AMFLoader: TextDecoder not present. Please use TextDecoder polyfill.' );
-				return null;
-
-			}
-
-			var fileText = new TextDecoder( 'utf-8' ).decode( view );
+			var fileText = THREE.LoaderUtils.decodeText( view );
 			var xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
 
 			if ( xmlData.documentElement.nodeName.toLowerCase() !== 'amf' ) {

+ 8 - 1
examples/js/loaders/AssimpJSONLoader.js

@@ -27,7 +27,7 @@ THREE.AssimpJSONLoader.prototype = {
 
 		var scope = this;
 
-		var path = THREE.Loader.prototype.extractUrlBase( url );
+		var path = THREE.LoaderUtils.extractUrlBase( url );
 
 		var loader = new THREE.FileLoader( this.manager );
 		loader.load( url, function ( text ) {
@@ -219,6 +219,13 @@ THREE.AssimpJSONLoader.prototype = {
 						material.flatShading = ( value === 1 ) ? true : false;
 						break;
 
+					case '$mat.opacity':
+						if ( value < 1 ) {
+							material.opacity = value;
+							material.transparent = true;
+						}
+						break;
+
 				}
 
 			}

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

@@ -18,7 +18,7 @@ THREE.AssimpLoader.prototype = {
 
 		var scope = this;
 
-		var path = THREE.Loader.prototype.extractUrlBase( url );
+		var path = THREE.LoaderUtils.extractUrlBase( url );
 
 		var loader = new THREE.FileLoader( this.manager );
 		loader.setResponseType( 'arraybuffer' );

+ 3 - 13
examples/js/loaders/BinaryLoader.js

@@ -32,8 +32,8 @@ THREE.BinaryLoader.prototype = {
 
 		// todo: unify load API to for easier SceneLoader use
 
-		var texturePath = this.texturePath || THREE.Loader.prototype.extractUrlBase( url );
-		var binaryPath = this.binaryPath || THREE.Loader.prototype.extractUrlBase( url );
+		var texturePath = this.texturePath || THREE.LoaderUtils.extractUrlBase( url );
+		var binaryPath = this.binaryPath || THREE.LoaderUtils.extractUrlBase( url );
 
 		// #1 load JS part via web worker
 
@@ -245,17 +245,7 @@ THREE.BinaryLoader.prototype = {
 
 			function parseString( data, offset, length ) {
 
-				var charArray = new Uint8Array( data, offset, length );
-
-				var text = "";
-
-				for ( var i = 0; i < length; i ++ ) {
-
-					text += String.fromCharCode( charArray[ i ] );
-
-				}
-
-				return text;
+				return THREE.LoaderUtils.decodeText( new Uint8Array( data, offset, length ) );
 
 			}
 

+ 46 - 5
examples/js/loaders/ColladaLoader.js

@@ -19,7 +19,7 @@ THREE.ColladaLoader.prototype = {
 
 		var scope = this;
 
-		var path = THREE.Loader.prototype.extractUrlBase( url );
+		var path = scope.path === undefined ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
 
 		var loader = new THREE.FileLoader( scope.manager );
 		loader.load( url, function ( text ) {
@@ -30,6 +30,12 @@ THREE.ColladaLoader.prototype = {
 
 	},
 
+	setPath: function ( value ) {
+
+		this.path = value;
+
+	},
+
 	options: {
 
 		set convertUpAxis( value ) {
@@ -1618,7 +1624,17 @@ THREE.ColladaLoader.prototype = {
 
 		function getCamera( id ) {
 
-			return getBuild( library.cameras[ id ], buildCamera );
+			var data = library.cameras[ id ];
+
+			if ( data !== undefined ) {
+
+				return getBuild( data, buildCamera );
+
+			}
+
+			console.warn( 'THREE.ColladaLoader: Couldn\'t find camera with ID:', id );
+
+			return null;
 
 		}
 
@@ -1743,7 +1759,17 @@ THREE.ColladaLoader.prototype = {
 
 		function getLight( id ) {
 
-			return getBuild( library.lights[ id ], buildLight );
+			var data = library.lights[ id ];
+
+			if ( data !== undefined ) {
+
+				return getBuild( data, buildLight );
+
+			}
+
+			console.warn( 'THREE.ColladaLoader: Couldn\'t find light with ID:', id );
+
+			return null;
 
 		}
 
@@ -1760,6 +1786,9 @@ THREE.ColladaLoader.prototype = {
 
 			var mesh = getElementsByTagName( xml, 'mesh' )[ 0 ];
 
+			// the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep
+			if ( mesh === undefined ) return;
+
 			for ( var i = 0; i < mesh.childNodes.length; i ++ ) {
 
 				var child = mesh.childNodes[ i ];
@@ -3028,7 +3057,13 @@ THREE.ColladaLoader.prototype = {
 
 			for ( var i = 0, l = instanceCameras.length; i < l; i ++ ) {
 
-				objects.push( getCamera( instanceCameras[ i ] ).clone() );
+				var instanceCamera = getCamera( instanceCameras[ i ] );
+
+				if ( instanceCamera !== null ) {
+
+					objects.push( instanceCamera.clone() );
+
+				}
 
 			}
 
@@ -3067,7 +3102,13 @@ THREE.ColladaLoader.prototype = {
 
 			for ( var i = 0, l = instanceLights.length; i < l; i ++ ) {
 
-				objects.push( getLight( instanceLights[ i ] ).clone() );
+				var instanceLight = getLight( instanceLights[ i ] );
+
+				if ( instanceLight !== null ) {
+
+					objects.push( instanceLight.clone() );
+
+				}
 
 			}
 

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

@@ -0,0 +1,348 @@
+/**
+ * @author Richard M. / https://github.com/richardmonette
+ */
+
+// https://github.com/mrdoob/three.js/issues/10652
+// https://en.wikipedia.org/wiki/OpenEXR
+
+THREE.EXRLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.EXRLoader.prototype = Object.create( THREE.DataTextureLoader.prototype );
+
+THREE.EXRLoader.prototype._parser = function ( buffer ) {
+
+	function parseNullTerminatedString( buffer, offset ) {
+
+		var uintBuffer = new Uint8Array( buffer );
+		var endOffset = 0;
+
+		while ( uintBuffer[ offset.value + endOffset ] != 0 ) {
+
+			endOffset += 1;
+
+		}
+
+		var stringValue = new TextDecoder().decode(
+			new Uint8Array( buffer ).slice( offset.value, offset.value + endOffset )
+		);
+
+		offset.value = offset.value + endOffset + 1;
+
+		return stringValue;
+
+	}
+
+	function parseFixedLengthString( buffer, offset, size ) {
+
+		var stringValue = new TextDecoder().decode(
+			new Uint8Array( buffer ).slice( offset.value, offset.value + size )
+		);
+
+		offset.value = offset.value + size;
+
+		return stringValue;
+
+	}
+
+	function parseUlong( buffer, offset ) {
+
+		var uLong = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );
+
+		offset.value = offset.value + 8;
+
+		return uLong;
+
+	}
+
+	function parseUint32( buffer, offset ) {
+
+		var Uint32 = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );
+
+		offset.value = offset.value + 4;
+
+		return Uint32;
+
+	}
+
+	function parseUint8( buffer, offset ) {
+
+		var Uint8 = new DataView( buffer.slice( offset.value, offset.value + 1 ) ).getUint8( 0, true );
+
+		offset.value = offset.value + 1;
+
+		return Uint8;
+
+	}
+
+	function parseFloat32( buffer, offset ) {
+
+		var float = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getFloat32( 0, true );
+
+		offset.value += 4;
+
+		return float;
+
+	}
+
+	// https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript
+	function decodeFloat16( binary ) {
+
+		var exponent = ( binary & 0x7C00 ) >> 10,
+			fraction = binary & 0x03FF;
+
+		return ( binary >> 15 ? - 1 : 1 ) * (
+			exponent ?
+				(
+					exponent === 0x1F ?
+						fraction ? NaN : Infinity :
+						Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 )
+				) :
+				6.103515625e-5 * ( fraction / 0x400 )
+		);
+
+	}
+
+	function parseFloat16( buffer, offset ) {
+
+		var float = new DataView( buffer.slice( offset.value, offset.value + 2 ) ).getUint16( 0, true );
+
+		offset.value += 2;
+
+		return decodeFloat16( float );
+
+	}
+
+	function parseChlist( buffer, offset, size ) {
+
+		var startOffset = offset.value;
+		var channels = [];
+
+		while ( offset.value < ( startOffset + size - 1 ) ) {
+
+			var name = parseNullTerminatedString( buffer, offset );
+			var pixelType = parseUint32( buffer, offset ); // TODO: Cast this to UINT, HALF or FLOAT
+			var pLinear = parseUint8( buffer, offset );
+			offset.value += 3; // reserved, three chars
+			var xSampling = parseUint32( buffer, offset );
+			var ySampling = parseUint32( buffer, offset );
+
+			channels.push( {
+				name: name,
+				pixelType: pixelType,
+				pLinear: pLinear,
+				xSampling: xSampling,
+				ySampling: ySampling
+			} );
+
+		}
+
+		offset.value += 1;
+
+		return channels;
+
+	}
+
+	function parseChromaticities( buffer, offset ) {
+
+		var redX = parseFloat32( buffer, offset );
+		var redY = parseFloat32( buffer, offset );
+		var greenX = parseFloat32( buffer, offset );
+		var greenY = parseFloat32( buffer, offset );
+		var blueX = parseFloat32( buffer, offset );
+		var blueY = parseFloat32( buffer, offset );
+		var whiteX = parseFloat32( buffer, offset );
+		var whiteY = parseFloat32( buffer, offset );
+
+		return { redX: redX, redY: redY, greenX, greenY, blueX, blueY, whiteX, whiteY };
+
+	}
+
+	function parseCompression( buffer, offset ) {
+
+		var compressionCodes = [
+			'NO_COMPRESSION',
+			'PIZ_COMPRESSION'
+		];
+
+		var compression = parseUint8( buffer, offset );
+
+		return compressionCodes[ compression ];
+
+	}
+
+	function parseBox2i( buffer, offset ) {
+
+		var xMin = parseUint32( buffer, offset );
+		var yMin = parseUint32( buffer, offset );
+		var xMax = parseUint32( buffer, offset );
+		var yMax = parseUint32( buffer, offset );
+
+		return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax };
+
+	}
+
+	function parseLineOrder( buffer, offset ) {
+
+		var lineOrders = [
+			'INCREASING_Y'
+		];
+
+		var lineOrder = parseUint8( buffer, offset );
+
+		return lineOrders[ lineOrder ];
+
+	}
+
+	function parseV2f( buffer, offset ) {
+
+		var x = parseFloat32( buffer, offset );
+		var y = parseFloat32( buffer, offset );
+
+		return [ x, y ];
+
+	}
+
+	function parseValue( buffer, offset, type, size ) {
+
+		if ( type == 'string' || type == 'iccProfile' ) {
+
+			return parseFixedLengthString( buffer, offset, size );
+
+		} else if ( type == 'chlist' ) {
+
+			return parseChlist( buffer, offset, size );
+
+		} else if ( type == 'chromaticities' ) {
+
+			return parseChromaticities( buffer, offset );
+
+		} else if ( type == 'compression' ) {
+
+			return parseCompression( buffer, offset );
+
+		} else if ( type == 'box2i' ) {
+
+			return parseBox2i( buffer, offset );
+
+		} else if ( type == 'lineOrder' ) {
+
+			return parseLineOrder( buffer, offset );
+
+		} else if ( type == 'float' ) {
+
+			return parseFloat32( buffer, offset );
+
+		} else if ( type == 'v2f' ) {
+
+			return parseV2f( buffer, offset );
+
+		} else {
+
+			throw 'Cannot parse value for unsupported type: ' + type;
+
+		}
+
+	}
+
+	var EXRHeader = {};
+
+	var magic = new DataView( buffer ).getUint32( 0, true );
+	var versionByteZero = new DataView( buffer ).getUint8( 4, true );
+	var fullMask = new DataView( buffer ).getUint8( 5, true );
+
+	// start of header
+
+	var offset = { value: 8 }; // start at 8, after magic stuff
+
+	var keepReading = true;
+
+	while ( keepReading ) {
+
+		var attributeName = parseNullTerminatedString( buffer, offset );
+
+		if ( attributeName == 0 ) {
+
+			keepReading = false;
+
+		} else {
+
+			var attributeType = parseNullTerminatedString( buffer, offset );
+			var attributeSize = parseUint32( buffer, offset );
+			var attributeValue = parseValue( buffer, offset, attributeType, attributeSize );
+
+			EXRHeader[ attributeName ] = attributeValue;
+
+		}
+
+	}
+
+	// offsets
+
+	var dataWindowHeight = EXRHeader.dataWindow.yMax + 1;
+	var scanlineBlockSize = 1; // 1 for no compression, 32 for PIZ
+	var numBlocks = dataWindowHeight / scanlineBlockSize;
+
+	for ( var i = 0; i < numBlocks; i ++ ) {
+
+		var scanlineOffset = parseUlong( buffer, offset );
+
+	}
+
+	// we should be passed the scanline offset table, start reading pixel data
+
+	var width = EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1;
+	var height = EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1;
+	var numChannels = EXRHeader.channels.length;
+
+	var byteArray = new Float32Array( width * height * numChannels );
+
+	var channelOffsets = {
+		R: 0,
+		G: 1,
+		B: 2,
+		A: 3
+	};
+
+	for ( var y = 0; y < height; y ++ ) {
+
+		var y_scanline = parseUint32( buffer, offset );
+		var dataSize = parseUint32( buffer, offset );
+
+		for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
+
+			if ( EXRHeader.channels[ channelID ].pixelType == 1 ) {
+
+				// HALF
+				for ( var x = 0; x < width; x ++ ) {
+
+					var val = parseFloat16( buffer, offset );
+					var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
+
+					byteArray[ ( ( ( width - y_scanline ) * ( height * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;
+
+				}
+
+			} else {
+
+				throw 'Only supported pixel format is HALF';
+
+			}
+
+		}
+
+	}
+
+	return {
+		header: EXRHeader,
+		width: width,
+		height: height,
+		data: byteArray,
+		format: THREE.RGBAFormat,
+		type: THREE.FloatType
+	};
+
+};

File diff suppressed because it is too large
+ 306 - 226
examples/js/loaders/FBXLoader.js


+ 226 - 0
examples/js/loaders/GCodeLoader.js

@@ -0,0 +1,226 @@
+'use strict';
+
+/**
+ * THREE.GCodeLoader is used to load gcode files usually used for 3D printing or CNC applications.
+ *
+ * Gcode files are composed by commands used by machines to create objects.
+ *
+ * @class THREE.GCodeLoader
+ * @param {Manager} manager Loading manager.
+ * @author tentone
+ * @author joewalnes
+ */
+THREE.GCodeLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+	this.splitLayer = false;
+
+};
+
+THREE.GCodeLoader.prototype.load = function ( url, onLoad, onProgress, onError ) {
+
+	var self = this;
+
+	var loader = new THREE.FileLoader( self.manager );
+	loader.load( url, function ( text ) {
+
+		onLoad( self.parse( text ) );
+
+	}, onProgress, onError );
+
+};
+
+THREE.GCodeLoader.prototype.parse = function ( data ) {
+
+	var state = { x: 0, y: 0, z: 0, e: 0, f: 0, extruding: false, relative: false };
+	var layers = [];
+
+	var currentLayer = undefined;
+
+	var box = new THREE.Box3();
+
+	var pathMaterial = new THREE.LineBasicMaterial( { color: 0xFF0000 } );
+	pathMaterial.name = 'path';
+
+	var extrudingMaterial = new THREE.LineBasicMaterial( { color: 0x00FF00 } );
+	extrudingMaterial.name = 'extruded';
+
+	function newLayer( line ) {
+
+		currentLayer = { vertex: [], pathVertex: [], z: line.z };
+		layers.push( currentLayer );
+
+	}
+
+	//Create lie segment between p1 and p2
+	function addSegment( p1, p2 ) {
+
+		if ( currentLayer === undefined ) {
+
+			newLayer( p1 );
+
+		}
+
+		if ( line.extruding ) {
+
+			currentLayer.vertex.push( p1.x, p1.y, p1.z );
+			currentLayer.vertex.push( p2.x, p2.y, p2.z );
+
+		} else {
+
+			currentLayer.pathVertex.push( p1.x, p1.y, p1.z );
+			currentLayer.pathVertex.push( p2.x, p2.y, p2.z );
+
+		}
+
+		if ( line.extruding ) {
+
+			box.min.set( Math.min( box.min.x, p2.x ), Math.min( box.min.y, p2.y ), Math.min( box.min.z, p2.z ) );
+			box.max.set( Math.max( box.max.x, p2.x ), Math.max( box.max.y, p2.y ), Math.max( box.max.z, p2.z ) );
+
+		}
+
+	}
+
+	function delta( v1, v2 ) {
+
+		return state.relative ? v2 : v2 - v1;
+
+	}
+
+	function absolute ( v1, v2 ) {
+
+		return state.relative ? v1 + v2 : v2;
+
+	}
+
+	var lines = data.replace( /;.+/g,'' ).split( '\n' );
+
+	for ( var i = 0; i < lines.length; i ++ ) {
+
+		var tokens = lines[ i ].split( ' ' );
+		var cmd = tokens[ 0 ].toUpperCase();
+
+		//Argumments
+		var args = {};
+		tokens.splice( 1 ).forEach( function ( token ) {
+
+			if ( token[ 0 ] !== undefined ) {
+
+				var key = token[ 0 ].toLowerCase();
+				var value = parseFloat( token.substring( 1 ) );
+				args[ key ] = value;
+
+			}
+
+		} );
+
+		//Process commands
+		//G0/G1 – Linear Movement
+		if ( cmd === 'G0' || cmd === 'G1' ) {
+
+			var line = {
+				x: args.x !== undefined ? absolute( state.x, args.x ) : state.x,
+				y: args.y !== undefined ? absolute( state.y, args.y ) : state.y,
+				z: args.z !== undefined ? absolute( state.z, args.z ) : state.z,
+				e: args.e !== undefined ? absolute( state.e, args.e ) : state.e,
+				f: args.f !== undefined ? absolute( state.f, args.f ) : state.f,
+			};
+
+			//Layer change detection is or made by watching Z, it's made by watching when we extrude at a new Z position
+			if ( delta( state.e, line.e ) > 0 ) {
+
+				line.extruding = delta( state.e, line.e ) > 0;
+
+				if ( currentLayer == undefined || line.z != currentLayer.z ) {
+
+					newLayer( line );
+
+				}
+
+			}
+
+			addSegment( state, line );
+			state = line;
+
+		} else if ( cmd === 'G2' || cmd === 'G3' ) {
+
+			//G2/G3 - Arc Movement ( G2 clock wise and G3 counter clock wise )
+			console.warn( 'THREE.GCodeLoader: Arc command not supported' );
+
+		} else if ( cmd === 'G90' ) {
+
+			//G90: Set to Absolute Positioning
+			state.relative = false;
+
+		} else if ( cmd === 'G91' ) {
+
+			//G91: Set to state.relative Positioning
+			state.relative = true;
+
+		} else if ( cmd === 'G92' ) {
+
+			//G92: Set Position
+			var line = state;
+			line.x = args.x !== undefined ? args.x : line.x;
+			line.y = args.y !== undefined ? args.y : line.y;
+			line.z = args.z !== undefined ? args.z : line.z;
+			line.e = args.e !== undefined ? args.e : line.e;
+			state = line;
+
+		} else {
+
+			console.warn( 'THREE.GCodeLoader: Command not supported:' + cmd );
+
+		}
+
+	}
+
+	function addObject( vertex, extruding ) {
+
+		var geometry = new THREE.BufferGeometry();
+		geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertex, 3 ) );
+
+		var segments = new THREE.LineSegments( geometry, extruding ? extrudingMaterial : pathMaterial );
+		segments.name = 'layer' + i;
+		object.add( segments );
+
+	}
+
+	var object = new THREE.Group();
+	object.name = 'gcode';
+
+	if ( this.splitLayer ) {
+
+		for ( var i = 0; i < layers.length; i ++ ) {
+
+			var layer = layers[ i ];
+			addObject( layer.vertex, true );
+			addObject( layer.pathVertex, false );
+
+		}
+
+	} else {
+
+		var vertex = [], pathVertex = [];
+
+		for ( var i = 0; i < layers.length; i ++ ) {
+
+			var layer = layers[ i ];
+
+			vertex = vertex.concat( layer.vertex );
+			pathVertex = pathVertex.concat( layer.pathVertex );
+
+		}
+
+		addObject( vertex, true );
+		addObject( pathVertex, false );
+
+	}
+
+	object.rotation.set( - Math.PI / 2, 0, 0 );
+
+	return object;
+
+};

File diff suppressed because it is too large
+ 470 - 356
examples/js/loaders/GLTFLoader.js


+ 0 - 147
examples/js/loaders/ImageBitmapLoader.js

@@ -1,147 +0,0 @@
-/**
- * @author thespite / http://clicktorelease.com/
- */
-
-function detectCreateImageBitmap ( optionsList ) {
-
-	var url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
-
-	return new Promise( function ( resolve, reject ) {
-
-		if ( ! ( 'createImageBitmap' in window ) ) {
-
-			reject();
-			return;
-
-		}
-
-		fetch( url ).then( function ( res ) {
-
-			return res.blob();
-
-		} ).then( function ( blob ) {
-
-			var pendingImages = [];
-
-			for ( var i = 0; i < optionsList.length; i ++ ) {
-
-				var pendingImage = optionsList[ i ] === undefined
-					? createImageBitmap( blob )
-					: createImageBitmap( blob, optionsList[ i ] );
-
-				pendingImages.push( pendingImage );
-
-			}
-
-			Promise.all( pendingImages ).then( function () {
-
-				resolve();
-
-			} ).catch( function () {
-
-				reject();
-
-			} );
-
-		} );
-
-	} );
-
-}
-
-var canUseImageBitmap = detectCreateImageBitmap( [ undefined ] );
-
-var canUseImageBitmapOptions = detectCreateImageBitmap( [
-	{ imageOrientation: 'none', premultiplyAlpha: 'none' },
-	{ imageOrientation: 'flipY', premultiplyAlpha: 'none' },
-	{ imageOrientation: 'none', premultiplyAlpha: 'premultiply' },
-	{ imageOrientation: 'flipY', premultiplyAlpha: 'premultiply' }
-] );
-
-
-THREE.ImageBitmapLoader = function ( manager ) {
-
-	canUseImageBitmap.catch( function () {
-
-		console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );
-
-	} );
-
-	this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager;
-	this.options = undefined;
-
-};
-
-THREE.ImageBitmapLoader.prototype = {
-
-	constructor: THREE.ImageBitmapLoader,
-
-	setOptions: function setOptions( options ) {
-
-		canUseImageBitmapOptions.catch( function () {
-
-			console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() options not supported.' );
-
-		} );
-
-		this.options = options;
-		return this;
-
-	},
-
-	load: function load( url, onLoad, onProgress, onError ) {
-
-		if ( url === undefined ) url = '';
-
-		if ( this.path !== undefined ) url = this.path + url;
-
-		var scope = this;
-
-		var cached = THREE.Cache.get( url );
-
-		if ( cached !== undefined ) {
-
-			scope.manager.itemStart( url );
-
-			setTimeout( function () {
-
-				if ( onLoad ) onLoad( cached );
-
-				scope.manager.itemEnd( url );
-
-			}, 0 );
-
-			return cached;
-
-		}
-
-		fetch( url ).then( function ( res ) {
-
-			return res.blob();
-
-		} ).then( function ( blob ) {
-
-			return scope.options === undefined
-				? createImageBitmap( blob )
-				: createImageBitmap( blob, scope.options );
-
-		} ).then( function ( imageBitmap ) {
-
-			THREE.Cache.add( url, imageBitmap );
-
-			if ( onLoad ) onLoad( imageBitmap );
-
-			scope.manager.itemEnd( url );
-
-		} ).catch( function ( e ) {
-
-			if ( onError ) onError( e );
-
-			scope.manager.itemEnd( url );
-			scope.manager.itemError( url );
-
-		} );
-
-	}
-
-};

+ 298 - 229
examples/js/loaders/LoaderSupport.js

@@ -277,7 +277,7 @@ THREE.LoaderSupport.ResourceDescriptor = (function () {
 		if ( urlParts.length < 2 ) {
 
 			this.path = null;
-			this.name = this.name = url;
+			this.name = url;
 			this.url = url;
 
 		} else {
@@ -941,13 +941,8 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () {
 	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
 	 */
 	WorkerRunnerRefImpl.prototype.processMessage = function ( payload ) {
-		var logger = new ConsoleLogger();
-		if ( Validator.isValid( payload.logger ) ) {
-
-			logger.setEnabled( payload.logger.enabled );
-			logger.setDebug( payload.logger.debug );
-
-		}
+		var logEnabled = payload.logger.enabled;
+		var logDebug = payload.logger.enabled;
 		if ( payload.cmd === 'run' ) {
 
 			var callbacks = {
@@ -955,19 +950,20 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () {
 					self.postMessage( payload );
 				},
 				callbackProgress: function ( text ) {
-					logger.logDebug( 'WorkerRunner: progress: ' + text );
+					if ( logEnabled && logDebug ) console.debug( 'WorkerRunner: progress: ' + text );
 				}
 			};
 
 			// Parser is expected to be named as such
-			var parser = new Parser( logger );
+			var parser = new Parser();
+			if ( typeof parser[ 'setLogConfig' ] === 'function' ) parser.setLogConfig( logEnabled, logDebug );
 			this.applyProperties( parser, payload.params );
 			this.applyProperties( parser, payload.materials );
 			this.applyProperties( parser, callbacks );
 			parser.workerScope = self;
 			parser.parse( payload.data.input, payload.data.options );
 
-			logger.logInfo( 'WorkerRunner: Run complete!' );
+			if ( logEnabled ) console.log( 'WorkerRunner: Run complete!' );
 
 			callbacks.callbackBuilder( {
 				cmd: 'complete',
@@ -976,7 +972,7 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () {
 
 		} else {
 
-			logger.logError( 'WorkerRunner: Received unknown command: ' + payload.cmd );
+			console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd );
 
 		}
 	};
@@ -993,170 +989,234 @@ THREE.LoaderSupport.WorkerRunnerRefImpl = (function () {
  */
 THREE.LoaderSupport.WorkerSupport = (function () {
 
-	var WORKER_SUPPORT_VERSION = '1.1.1';
+	var WORKER_SUPPORT_VERSION = '2.0.1';
 
 	var Validator = THREE.LoaderSupport.Validator;
 
-	function WorkerSupport( logger ) {
-		this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
-		this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION );
+	var LoaderWorker = (function () {
 
-		// check worker support first
-		if ( window.Worker === undefined ) throw "This browser does not support web workers!";
-		if ( window.Blob === undefined  ) throw "This browser does not support Blob!";
-		if ( typeof window.URL.createObjectURL !== 'function'  ) throw "This browser does not support Object creation from URL!";
+		function LoaderWorker( logger ) {
+			this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
+			this._reset();
+		}
+
+		LoaderWorker.prototype._reset = function () {
+			this.worker = null;
+			this.runnerImplName = null;
+			this.callbacks = {
+				builder: null,
+				onLoad: null
+			};
+			this.terminateRequested = false;
+			this.queuedMessage = null;
+			this.started = false;
+		};
+
+		LoaderWorker.prototype.initWorker = function ( code, runnerImplName ) {
+			this.runnerImplName = runnerImplName;
+			var blob = new Blob( [ code ], { type: 'application/javascript' } );
+			this.worker = new Worker( window.URL.createObjectURL( blob ) );
+			this.worker.onmessage = this._receiveWorkerMessage;
 
-		this.worker = null;
-		this.workerCode = null;
-		this.loading = true;
-		this.queuedMessage = null;
-		this.running = false;
-		this.terminateRequested = false;
+			// set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members
+			this.worker.runtimeRef = this;
 
-		this.callbacks = {
-			builder: null,
-			onLoad: null
+			// process stored queuedMessage
+			this._postMessage();
 		};
-	}
 
-	/**
-	 * Validate the status of worker code and the derived worker.
-	 * @memberOf THREE.LoaderSupport.WorkerSupport
-	 *
-	 * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons.
-	 * @param {boolean} forceWorkerReload Force re-build of the worker code.
-	 * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath
-	 * @param {String} libPath Base path used for loading libraries
-	 * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here.
-	 */
-	WorkerSupport.prototype.validate = function ( functionCodeBuilder, forceWorkerReload, libLocations, libPath, runnerImpl ) {
-		this.running = false;
-		if ( forceWorkerReload ) {
+		/**
+		 * Executed in worker scope
+ 		 */
+		LoaderWorker.prototype._receiveWorkerMessage = function ( e ) {
+			var payload = e.data;
+			switch ( payload.cmd ) {
+				case 'meshData':
+				case 'materialData':
+				case 'imageData':
+					this.runtimeRef.callbacks.builder( payload );
+					break;
 
-			this.worker = null;
-			this.workerCode = null;
-			this.loading = true;
-			this.queuedMessage = null;
-			this.callbacks.builder = null;
-			this.callbacks.onLoad = null;
+				case 'complete':
+					this.runtimeRef.queuedMessage = null;
+					this.started = false;
+					this.runtimeRef.callbacks.onLoad( payload.msg );
 
-		}
+					if ( this.runtimeRef.terminateRequested ) {
 
-		if ( ! Validator.isValid( this.worker ) ) {
+						this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' );
+						this.runtimeRef._terminate();
 
-			this.logger.logInfo( 'WorkerSupport: Building worker code...' );
-			this.logger.logTimeStart( 'buildWebWorkerCode' );
+					}
+					break;
 
-			var workerRunner;
-			if ( Validator.isValid( runnerImpl ) ) {
+				case 'error':
+					this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg );
+					this.runtimeRef.queuedMessage = null;
+					this.started = false;
+					this.runtimeRef.callbacks.onLoad( payload.msg );
 
-				this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runncer class for worker.' );
-				workerRunner = runnerImpl;
+					if ( this.runtimeRef.terminateRequested ) {
 
-			} else {
+						this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' );
+						this.runtimeRef._terminate();
 
-				this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runncer class for worker.' );
-				workerRunner = THREE.LoaderSupport.WorkerRunnerRefImpl;
+					}
+					break;
+
+				default:
+					this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd );
+					break;
 
 			}
+		};
 
-			var scope = this;
-			var buildWorkerCode = function ( baseWorkerCode ) {
-				scope.workerCode = baseWorkerCode;
-				if ( workerRunner == THREE.LoaderSupport.WorkerRunnerRefImpl ) {
+		LoaderWorker.prototype.setCallbacks = function ( builder, onLoad ) {
+			this.callbacks.builder = Validator.verifyInput( builder, this.callbacks.builder );
+			this.callbacks.onLoad = Validator.verifyInput( onLoad, this.callbacks.onLoad );
+		};
 
-					scope.workerCode += buildObject( 'Validator', THREE.LoaderSupport.Validator );
-					scope.workerCode += buildSingelton( 'ConsoleLogger', 'ConsoleLogger', THREE.LoaderSupport.ConsoleLogger );
+		LoaderWorker.prototype.run = function( payload ) {
+			if ( Validator.isValid( this.queuedMessage ) ) {
 
-				}
-				scope.workerCode += functionCodeBuilder( buildObject, buildSingelton );
-				scope.workerCode += buildSingelton( workerRunner.name, workerRunner.name, workerRunner );
-				scope.workerCode += 'new ' + workerRunner.name + '();\n\n';
+				console.warn( 'Already processing message. Rejecting new run instruction' );
+				return;
 
-				var blob = new Blob( [ scope.workerCode ], { type: 'application/javascript' } );
-				scope.worker = new Worker( window.URL.createObjectURL( blob ) );
-				scope.logger.logTimeEnd( 'buildWebWorkerCode' );
+			} else {
 
-				var receiveWorkerMessage = function ( e ) {
-					var payload = e.data;
+				this.queuedMessage = payload;
+				this.started = true;
 
-					switch ( payload.cmd ) {
-						case 'meshData':
-						case 'materialData':
-						case 'imageData':
-							scope.callbacks.builder( payload );
-							break;
+			}
+			if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.';
+			if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.';
+			if ( payload.cmd !== 'run' ) payload.cmd = 'run';
+			if ( Validator.isValid( payload.logger ) ) {
 
-						case 'complete':
-							scope.callbacks.onLoad( payload.msg );
-							scope.running = false;
+				payload.logger.enabled = Validator.verifyInput( payload.logger.enabled, true );
+				payload.logger.debug = Validator.verifyInput( payload.logger.debug, false );
 
-							if ( scope.terminateRequested ) {
+			} else {
 
-								scope.logger.logInfo( 'WorkerSupport [' + workerRunner + ']: Run is complete. Terminating application on request!' );
-								scope.terminateWorker();
+				payload.logger = {
+					enabled: true,
+					debug: false
+				}
 
-							}
-							break;
+			}
+			this._postMessage();
+		};
 
-						case 'error':
-							scope.logger.logError( 'WorkerSupport [' + workerRunner + ']: Reported error: ' + payload.msg );
-							break;
+		LoaderWorker.prototype._postMessage = function () {
+			if ( Validator.isValid( this.queuedMessage ) && Validator.isValid( this.worker ) ) {
 
-						default:
-							scope.logger.logError( 'WorkerSupport [' + workerRunner + ']: Received unknown command: ' + payload.cmd );
-							break;
+				if ( this.queuedMessage.data.input instanceof ArrayBuffer ) {
 
-					}
-				};
-				scope.worker.addEventListener( 'message', receiveWorkerMessage, false );
-				scope.loading = false;
-				scope._postMessage();
-			};
+					this.worker.postMessage( this.queuedMessage, [ this.queuedMessage.data.input ] );
 
-			if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) {
+				} else {
 
-				var libsContent = '';
-				var loadAllLibraries = function ( path, locations ) {
-					if ( locations.length === 0 ) {
+					this.worker.postMessage( this.queuedMessage );
 
-						buildWorkerCode( libsContent );
+				}
 
-					} else {
+			}
+		};
 
-						var loadedLib = function ( contentAsString ) {
-							libsContent += contentAsString;
-							loadAllLibraries( path, locations );
-						};
+		LoaderWorker.prototype.setTerminateRequested = function ( terminateRequested ) {
+			this.terminateRequested = terminateRequested === true;
+			if ( this.terminateRequested && Validator.isValid( this.worker ) && ! Validator.isValid( this.queuedMessage ) && this.started ) {
 
-						var fileLoader = new THREE.FileLoader();
-						fileLoader.setPath( path );
-						fileLoader.setResponseType( 'text' );
-						fileLoader.load( locations[ 0 ], loadedLib );
-						locations.shift();
+				this.logger.logInfo( 'Worker is terminated immediately as it is not running!' );
+				this._terminate();
 
-					}
-				};
-				loadAllLibraries( libPath, libLocations );
+			}
+		};
 
-			} else {
+		LoaderWorker.prototype._terminate = function () {
+			this.worker.terminate();
+			this._reset();
+		};
 
-				buildWorkerCode( '' );
+		return LoaderWorker;
 
-			}
-		}
-	};
+	})();
+
+	function WorkerSupport( logger ) {
+		this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() );
+		this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION );
+
+		// check worker support first
+		if ( window.Worker === undefined ) throw "This browser does not support web workers!";
+		if ( window.Blob === undefined  ) throw "This browser does not support Blob!";
+		if ( typeof window.URL.createObjectURL !== 'function'  ) throw "This browser does not support Object creation from URL!";
+
+		this.loaderWorker = new LoaderWorker( this.logger );
+	}
 
 	/**
-	 * Terminate the worker and the code.
+	 * Validate the status of worker code and the derived worker.
 	 * @memberOf THREE.LoaderSupport.WorkerSupport
+	 *
+	 * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons.
+	 * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath
+	 * @param {String} libPath Base path used for loading libraries
+	 * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here.
 	 */
-	WorkerSupport.prototype.terminateWorker = function () {
-		if ( Validator.isValid( this.worker ) ) {
-			this.worker.terminate();
+	WorkerSupport.prototype.validate = function ( functionCodeBuilder, libLocations, libPath, runnerImpl ) {
+		if ( Validator.isValid( this.loaderWorker.worker ) ) return;
+
+		this.logger.logInfo( 'WorkerSupport: Building worker code...' );
+		this.logger.logTimeStart( 'buildWebWorkerCode' );
+
+		if ( Validator.isValid( runnerImpl ) ) {
+
+			this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runncer class for worker.' );
+
+		} else {
+
+			runnerImpl = THREE.LoaderSupport.WorkerRunnerRefImpl;
+			this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runncer class for worker.' );
+
+		}
+
+		var userWorkerCode = functionCodeBuilder( buildObject, buildSingelton );
+		userWorkerCode += buildSingelton( runnerImpl.name, runnerImpl.name, runnerImpl );
+		userWorkerCode += 'new ' + runnerImpl.name + '();\n\n';
+
+		var scope = this;
+		if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) {
+
+			var libsContent = '';
+			var loadAllLibraries = function ( path, locations ) {
+				if ( locations.length === 0 ) {
+
+					scope.loaderWorker.initWorker( libsContent + userWorkerCode, scope.logger, runnerImpl.name );
+					scope.logger.logTimeEnd( 'buildWebWorkerCode' );
+
+				} else {
+
+					var loadedLib = function ( contentAsString ) {
+						libsContent += contentAsString;
+						loadAllLibraries( path, locations );
+					};
+
+					var fileLoader = new THREE.FileLoader();
+					fileLoader.setPath( path );
+					fileLoader.setResponseType( 'text' );
+					fileLoader.load( locations[ 0 ], loadedLib );
+					locations.shift();
+
+				}
+			};
+			loadAllLibraries( libPath, libLocations );
+
+		} else {
+
+			this.loaderWorker.initWorker( userWorkerCode, this.logger, runnerImpl.name );
+			this.logger.logTimeEnd( 'buildWebWorkerCode' );
+
 		}
-		this.worker = null;
-		this.workerCode = null;
 	};
 
 	/**
@@ -1167,10 +1227,27 @@ THREE.LoaderSupport.WorkerSupport = (function () {
 	 * @param {Function} onLoad The function that is called when parsing is complete.
 	 */
 	WorkerSupport.prototype.setCallbacks = function ( builder, onLoad ) {
-		this.callbacks = {
-			builder: builder,
-			onLoad: onLoad
-		};
+		this.loaderWorker.setCallbacks( builder, onLoad );
+	};
+
+	/**
+	 * Runs the parser with the provided configuration.
+	 * @memberOf THREE.LoaderSupport.WorkerSupport
+	 *
+	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
+	 */
+	WorkerSupport.prototype.run = function ( payload ) {
+		this.loaderWorker.run( payload );
+	};
+
+	/**
+	 * Request termination of worker once parser is finished.
+	 * @memberOf THREE.LoaderSupport.WorkerSupport
+	 *
+	 * @param {boolean} terminateRequested True or false.
+	 */
+	WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) {
+		this.loaderWorker.setTerminateRequested( terminateRequested );
 	};
 
 	var buildObject = function ( fullName, object ) {
@@ -1229,41 +1306,8 @@ THREE.LoaderSupport.WorkerSupport = (function () {
 		return objectString;
 	};
 
-	/**
-	 * Request termination of worker once parser is finished.
-	 * @memberOf THREE.LoaderSupport.WorkerSupport
-	 *
-	 * @param {boolean} terminateRequested True or false.
-	 */
-	WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) {
-		this.terminateRequested = terminateRequested === true;
-	};
-
-	/**
-	 * Runs the parser with the provided configuration.
-	 * @memberOf THREE.LoaderSupport.WorkerSupport
-	 *
-	 * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes.
-	 */
-	WorkerSupport.prototype.run = function ( payload ) {
-		if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.';
-		if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.';
-		if ( Validator.isValid( this.worker ) || this.loading ) {
-			if ( payload.cmd !== 'run' ) payload.cmd = 'run';
-			this.queuedMessage = payload;
-			this.running = true;
-			this._postMessage();
-
-		}
-	};
-
-	WorkerSupport.prototype._postMessage = function () {
-		if ( ! this.loading && Validator.isValid( this.queuedMessage ) ) {
-			this.worker.postMessage( this.queuedMessage );
-		}
-	};
-
 	return WorkerSupport;
+
 })();
 
 /**
@@ -1272,7 +1316,7 @@ THREE.LoaderSupport.WorkerSupport = (function () {
  *   prepareWorkers
  *   enqueueForRun
  *   processQueue
- *   deregister
+ *   tearDown (to force stop)
  *
  * @class
  *
@@ -1281,7 +1325,7 @@ THREE.LoaderSupport.WorkerSupport = (function () {
  */
 THREE.LoaderSupport.WorkerDirector = (function () {
 
-	var LOADER_WORKER_DIRECTOR_VERSION = '2.0.0';
+	var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0';
 
 	var Validator = THREE.LoaderSupport.Validator;
 
@@ -1301,10 +1345,13 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 		this.workerDescription = {
 			classDef: classDef,
 			globalCallbacks: {},
-			workerSupports: []
+			workerSupports: {}
 		};
 		this.objectsCompleted = 0;
 		this.instructionQueue = [];
+		this.instructionQueuePointer = 0;
+
+		this.callbackOnFinishedProcessing = null;
 	}
 
 	/**
@@ -1349,30 +1396,21 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 		if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks;
 		this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE );
 		this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER );
+		this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize );
 		this.objectsCompleted = 0;
 		this.instructionQueue = [];
+		this.instructionQueuePointer = 0;
 
-		var start = this.workerDescription.workerSupports.length;
-		var i;
-		if ( start < this.maxWebWorkers ) {
-
-			for ( i = start; i < this.maxWebWorkers; i++ ) {
-
-				this.workerDescription.workerSupports[ i ] = {
-					workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ),
-					loader: null
-				};
-
-			}
-
-		} else {
-
-			for ( i = start - 1; i >= this.maxWebWorkers; i-- ) {
+		for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) {
 
-				this.workerDescription.workerSupports[ i ].workerSupport.setRequestTerminate( true );
-				this.workerDescription.workerSupports.pop();
+			this.workerDescription.workerSupports[ instanceNo ] = {
+				instanceNo: instanceNo,
+				inUse: false,
+				terminateRequested: false,
+				workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ),
+				loader: null
+			};
 
-			}
 		}
 	};
 
@@ -1388,47 +1426,68 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 		}
 	};
 
+	/**
+	 * Returns if any workers are running.
+	 *
+	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 * @returns {boolean}
+	 */
+	WorkerDirector.prototype.isRunning = function () {
+		var wsKeys = Object.keys( this.workerDescription.workerSupports );
+		return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 );
+	};
+
 	/**
 	 * Process the instructionQueue until it is depleted.
 	 * @memberOf THREE.LoaderSupport.WorkerDirector
 	 */
 	WorkerDirector.prototype.processQueue = function () {
-		if ( this.instructionQueue.length === 0 ) return;
+		var prepData, supportDesc;
+		for ( var instanceNo in this.workerDescription.workerSupports ) {
 
-		var length = Math.min( this.maxWebWorkers, this.instructionQueue.length );
-		for ( var i = 0; i < length; i++ ) {
+			supportDesc = this.workerDescription.workerSupports[ instanceNo ];
+			if ( ! supportDesc.inUse ) {
 
-			this._kickWorkerRun( this.instructionQueue[ 0 ], i );
-			this.instructionQueue.shift();
+				if ( this.instructionQueuePointer < this.instructionQueue.length ) {
 
-		}
-	};
+					prepData = this.instructionQueue[ this.instructionQueuePointer ];
+					this._kickWorkerRun( prepData, supportDesc );
+					this.instructionQueuePointer++;
 
-	WorkerDirector.prototype._kickWorkerRun = function( prepData, workerInstanceNo ) {
-		var scope = this;
-		var directorOnLoad = function ( event ) {
-			scope.objectsCompleted++;
+				} else {
 
-			var nextPrepData = scope.instructionQueue[ 0 ];
-			if ( Validator.isValid( nextPrepData ) ) {
+					this._deregister( supportDesc );
 
-				scope.instructionQueue.shift();
-				scope.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + scope.instructionQueue.length + ')\n\n' );
-				scope._kickWorkerRun( nextPrepData, event.detail.instanceNo );
+				}
 
-			} else if ( scope.instructionQueue.length === 0 ) {
+			}
 
-				scope.deregister();
+		}
 
-			}
-		};
+		if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) {
 
+			this.callbackOnFinishedProcessing();
+			this.callbackOnFinishedProcessing = null;
+
+		}
+	};
+
+	WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) {
+		supportDesc.inUse = true;
+		supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested );
+
+		this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' );
+
+		var scope = this;
 		var prepDataCallbacks = prepData.getCallbacks();
 		var globalCallbacks = this.workerDescription.globalCallbacks;
 		var wrapperOnLoad = function ( event ) {
 			if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event );
 			if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event );
-			directorOnLoad( event );
+			scope.objectsCompleted++;
+			supportDesc.inUse = false;
+
+			scope.processQueue();
 		};
 
 		var wrapperOnProgress = function ( event ) {
@@ -1441,8 +1500,7 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 			if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event );
 		};
 
-		var supportTuple = this.workerDescription.workerSupports[ workerInstanceNo ];
-		supportTuple.loader = this._buildLoader( workerInstanceNo );
+		supportDesc.loader = this._buildLoader( supportDesc.instanceNo );
 
 		var updatedCallbacks = new THREE.LoaderSupport.Callbacks();
 		updatedCallbacks.setCallbackOnLoad( wrapperOnLoad );
@@ -1450,13 +1508,13 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 		updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter );
 		prepData.callbacks = updatedCallbacks;
 
-		supportTuple.loader.run( prepData, supportTuple.workerSupport );
+		supportDesc.loader.run( prepData, supportDesc.workerSupport );
 	};
 
 	WorkerDirector.prototype._buildLoader = function ( instanceNo ) {
 		var classDef = this.workerDescription.classDef;
 		var loader = Object.create( classDef.prototype );
-		this.workerDescription.classDef.call( loader, null, this.logger );
+		this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger );
 
 		// verify that all required functions are implemented
 		if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".';
@@ -1466,36 +1524,47 @@ THREE.LoaderSupport.WorkerDirector = (function () {
 
 			throw classDef.name + ' has no property "workerSupport".';
 
-		} else if ( ! classDef.workerSupport instanceof THREE.LoaderSupport.WorkerSupport ) {
-
-			throw classDef.name + '.workerSupport is not of type "THREE.LoaderSupport.WorkerSupport".';
-
 		}
 		if ( typeof loader.run !== 'function'  ) throw classDef.name + ' has no function "run".';
+		if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) {
+
+			this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' );
+			loader.callbacks = new THREE.LoaderSupport.Callbacks();
 
+		}
 		return loader;
 	};
 
+	WorkerDirector.prototype._deregister = function ( supportDesc ) {
+		if ( Validator.isValid( supportDesc ) ) {
+
+			supportDesc.workerSupport.setTerminateRequested( true );
+			this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' );
+
+			var loaderCallbacks = supportDesc.loader.callbacks;
+			if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
+			delete this.workerDescription.workerSupports[ supportDesc.instanceNo ];
+
+		}
+	};
+
 	/**
 	 * Terminate all workers.
 	 * @memberOf THREE.LoaderSupport.WorkerDirector
+	 *
+	 * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing.
 	 */
-	WorkerDirector.prototype.deregister = function () {
+	WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) {
 		this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' );
 
-		for ( var i = 0, length = this.workerDescription.workerSupports.length; i < length; i++ ) {
+		this.instructionQueuePointer = this.instructionQueue.length;
+		this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null );
 
-			var supportTuple = this.workerDescription.workerSupports[ i ];
-			supportTuple.workerSupport.setTerminateRequested( true );
-			this.logger.logInfo( 'Requested termination of worker.' );
+		for ( var name in this.workerDescription.workerSupports ) {
 
-			var loaderCallbacks = supportTuple.loader.callbacks;
-			if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } );
+			this.workerDescription.workerSupports[ name ].terminateRequested = true;
 
 		}
-
-		this.workerDescription.workerSupports = [];
-		this.instructionQueue = [];
 	};
 
 	return WorkerDirector;

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

@@ -101,7 +101,7 @@ THREE.MMDLoader.prototype.loadModel = function ( url, callback, onProgress, onEr
 
 	var scope = this;
 
-	var texturePath = this.extractUrlBase( url );
+	var texturePath = THREE.LoaderUtils.extractUrlBase( url );
 	var modelExtension = this.extractExtension( url );
 
 	this.loadFileAsBuffer( url, function ( buffer ) {

+ 84 - 126
examples/js/loaders/OBJLoader2.js

@@ -7,6 +7,8 @@
 
 if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
 
+if ( THREE.LoaderSupport === undefined ) console.error( '"THREE.LoaderSupport" is not available. "THREE.OBJLoader2" requires it. Please include "LoaderSupport.js" in your HTML.' );
+
 /**
  * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer
  * @class
@@ -16,9 +18,10 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  */
 THREE.OBJLoader2 = (function () {
 
-	var OBJLOADER2_VERSION = '2.1.2';
+	var OBJLOADER2_VERSION = '2.2.1';
 	var LoaderBase = THREE.LoaderSupport.LoaderBase;
 	var Validator = THREE.LoaderSupport.Validator;
+	var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
 
 	OBJLoader2.prototype = Object.create( THREE.LoaderSupport.LoaderBase.prototype );
 	OBJLoader2.prototype.constructor = OBJLoader2;
@@ -126,10 +129,6 @@ THREE.OBJLoader2 = (function () {
 			this.workerSupport = workerSupportExternal;
 			this.logger = workerSupportExternal.logger;
 
-		} else {
-
-			this.terminateWorkerOnLoad = true;
-
 		}
 		var scope = this;
 		var onMaterialsLoaded = function ( materials ) {
@@ -176,7 +175,8 @@ THREE.OBJLoader2 = (function () {
 	OBJLoader2.prototype.parse = function ( content ) {
 		this.logger.logTimeStart( 'OBJLoader2 parse: ' + this.modelName );
 
-		var parser = new Parser( this.logger );
+		var parser = new Parser();
+		parser.setLogConfig( this.logger.enabled, this.logger.debug );
 		parser.setMaterialPerSmoothingGroup( this.materialPerSmoothingGroup );
 		parser.setUseIndices( this.useIndices );
 		parser.setDisregardNormals( this.disregardNormals );
@@ -239,7 +239,6 @@ THREE.OBJLoader2 = (function () {
 					}
 				}
 			);
-			if ( scope.terminateWorkerOnLoad ) scope.workerSupport.terminateWorker();
 			scope.logger.logTimeEnd( 'OBJLoader2 parseAsync: ' + scope.modelName );
 		};
 		var scopedOnMeshLoaded = function ( payload ) {
@@ -257,6 +256,8 @@ THREE.OBJLoader2 = (function () {
 			workerCode += '/**\n';
 			workerCode += '  * This code was constructed by OBJLoader2 buildCode.\n';
 			workerCode += '  */\n\n';
+			workerCode += funcBuildObject( 'Validator', Validator );
+			workerCode += funcBuildSingelton( 'ConsoleLogger', 'ConsoleLogger', ConsoleLogger );
 			workerCode += funcBuildSingelton( 'LoaderBase', 'LoaderBase', LoaderBase );
 			workerCode += funcBuildObject( 'Consts', Consts );
 			workerCode += funcBuildSingelton( 'Parser', 'Parser', Parser );
@@ -267,6 +268,7 @@ THREE.OBJLoader2 = (function () {
 		};
 		this.workerSupport.validate( buildCode, false );
 		this.workerSupport.setCallbacks( scopedOnMeshLoaded, scopedOnLoad );
+		if ( scope.terminateWorkerOnLoad ) this.workerSupport.setTerminateRequested( true );
 
 		var materialNames = {};
 		var materials = this.builder.getMaterials();
@@ -295,8 +297,7 @@ THREE.OBJLoader2 = (function () {
 					input: content,
 					options: null
 				}
-			},
-			[ content.buffer ]
+			}
 		);
 	};
 
@@ -330,7 +331,9 @@ THREE.OBJLoader2 = (function () {
 	 */
 	var Parser = (function () {
 
-		function Parser( logger ) {
+		var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
+
+		function Parser() {
 			this.callbackProgress = null;
 			this.callbackBuilder = null;
 
@@ -348,9 +351,8 @@ THREE.OBJLoader2 = (function () {
 				faces: 0,
 				doubleIndicesCount: 0
 			};
-			this.logger = logger;
+			this.logger = new ConsoleLogger();
 			this.totalBytes = 0;
-			this.reachedFaces = false;
 		};
 
 		Parser.prototype.setUseAsync = function ( useAsync ) {
@@ -383,6 +385,11 @@ THREE.OBJLoader2 = (function () {
 			this.callbackProgress = callbackProgress;
 		};
 
+		Parser.prototype.setLogConfig = function ( enabled, debug ) {
+			this.logger.setEnabled( enabled );
+			this.logger.setDebug( debug );
+		};
+
 		Parser.prototype.configure = function () {
 			this.rawMesh = new RawMesh( this.materialPerSmoothingGroup, this.useIndices, this.disregardNormals );
 
@@ -551,28 +558,7 @@ THREE.OBJLoader2 = (function () {
 
 			switch ( buffer[ 0 ] ) {
 				case Consts.LINE_V:
-					// object complete instance required if reached faces already (= reached next block of v)
-					if ( this.reachedFaces ) {
-
-						if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) {
-
-							throw 'Vertex Colors were detected, but vertex count and color count do not match!';
-
-						}
-						// only when new vertices after faces are detected complete new mesh is prepared (reset offsets, etc)
-						this.processCompletedMesh( this.rawMesh.objectName, this.rawMesh.groupName, currentByte, true );
-						this.reachedFaces = false;
-
-					}
-					if ( bufferPointer === 4 ) {
-
-						this.rawMesh.pushVertex( buffer )
-
-					} else {
-
-						this.rawMesh.pushVertexAndVertextColors( buffer );
-
-					}
+					this.rawMesh.pushVertex( buffer, bufferPointer > 4 );
 					break;
 
 				case Consts.LINE_VT:
@@ -584,7 +570,6 @@ THREE.OBJLoader2 = (function () {
 					break;
 
 				case Consts.LINE_F:
-					this.reachedFaces = true;
 					this.rawMesh.processFaces( buffer, bufferPointer, countSlashes( slashSpacePattern, slashSpacePatternPointer ) );
 					break;
 
@@ -598,12 +583,15 @@ THREE.OBJLoader2 = (function () {
 					break;
 
 				case Consts.LINE_G:
-					this.processCompletedMesh( this.rawMesh.objectName, concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), currentByte, false );
+					// 'g' leads to creation of mesh if valid data (faces declaration was done before), otherwise only groupName gets set
+					this.processCompletedMesh( currentByte );
+					this.rawMesh.pushGroup( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) );
 					flushStringBuffer( buffer, bufferPointer );
 					break;
 
 				case Consts.LINE_O:
-					this.processCompletedMesh( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), this.rawMesh.groupName, currentByte, false );
+					// 'o' is pure meta-information and does not result in creation of new meshes
+					this.rawMesh.pushObject( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ) );
 					flushStringBuffer( buffer, bufferPointer );
 					break;
 
@@ -636,43 +624,40 @@ THREE.OBJLoader2 = (function () {
 				'\n\tReal RawMeshSubGroup count: ' + report.subGroups;
 		};
 
-		Parser.prototype.processCompletedMesh = function ( objectName, groupName, currentByte, beginNewObject ) {
+		Parser.prototype.processCompletedMesh = function ( currentByte ) {
 			var result = this.rawMesh.finalize();
 			if ( Validator.isValid( result ) ) {
 
-				this.inputObjectCount++;
+				if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) {
+
+					throw 'Vertex Colors were detected, but vertex count and color count do not match!';
+
+				}
 				if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
+				this.inputObjectCount++;
+
 				this.buildMesh( result, currentByte );
 				var progressBytesPercent = currentByte / this.totalBytes;
 				this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
-				this.rawMesh = beginNewObject ? this.rawMesh.newInstanceResetOffsets() : this.rawMesh.newInstanceKeepOffsets();
+				this.rawMesh.reset( this.rawMesh.smoothingGroup.splitMaterials );
+
+				return true;
 
+			} else {
+
+				return false;
 			}
-			// Always update group an object name in case they have changed and are valid
-			if ( this.rawMesh.objectName !== objectName && Validator.isValid( objectName ) ) this.rawMesh.pushObject( objectName );
-			if ( this.rawMesh.groupName !== groupName && Validator.isValid( groupName ) ) this.rawMesh.pushGroup( groupName );
 		};
 
 		Parser.prototype.finalize = function ( currentByte ) {
 			this.logger.logInfo( 'Global output object count: ' + this.outputObjectCount );
-			var result = Validator.isValid( this.rawMesh ) ? this.rawMesh.finalize() : null;
-			if ( Validator.isValid( result ) ) {
+			if ( this.processCompletedMesh( currentByte ) && this.logger.isEnabled() ) {
 
-				this.inputObjectCount++;
-				if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
-				this.buildMesh( result, currentByte );
-
-				if ( this.logger.isEnabled() ) {
-
-					var parserFinalReport = 'Overall counts: ' +
-						'\n\tVertices: ' + this.counts.vertices +
-						'\n\tFaces: ' + this.counts.faces +
-						'\n\tMultiple definitions: ' + this.counts.doubleIndicesCount;
-					this.logger.logInfo( parserFinalReport );
-
-				}
-				var progressBytesPercent = currentByte / this.totalBytes;
-				this.callbackProgress( 'Completed Parsing: 100.00%', progressBytesPercent );
+				var parserFinalReport = 'Overall counts: ' +
+					'\n\tVertices: ' + this.counts.vertices +
+					'\n\tFaces: ' + this.counts.faces +
+					'\n\tMultiple definitions: ' + this.counts.doubleIndicesCount;
+				this.logger.logInfo( parserFinalReport );
 
 			}
 		};
@@ -827,11 +812,11 @@ THREE.OBJLoader2 = (function () {
 				if ( this.logger.isDebug() ) {
 					var materialIndexLine = Validator.isValid( selectedMaterialIndex ) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : '';
 					var createdReport = 'Output Object no.: ' + this.outputObjectCount +
-						'\n\t\tobjectName: ' + rawObjectDescription.objectName +
 						'\n\t\tgroupName: ' + rawObjectDescription.groupName +
-						'\n\t\tmaterialName: ' + rawObjectDescription.materialName +
 						materialIndexLine +
+						'\n\t\tmaterialName: ' + rawObjectDescription.materialName +
 						'\n\t\tsmoothingGroup: ' + rawObjectDescription.smoothingGroup +
+						'\n\t\tobjectName: ' + rawObjectDescription.objectName +
 						'\n\t\t#vertices: ' + rawObjectDescription.vertices.length / 3 +
 						'\n\t\t#indices: ' + rawObjectDescription.indices.length +
 						'\n\t\t#colors: ' + rawObjectDescription.colors.length / 3 +
@@ -883,82 +868,51 @@ THREE.OBJLoader2 = (function () {
 	 */
 	var RawMesh = (function () {
 
-		function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals, activeMtlName ) {
-			this.globalVertexOffset = 1;
-			this.globalUvOffset = 1;
-			this.globalNormalOffset = 1;
-
+		function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals ) {
 			this.vertices = [];
 			this.colors = [];
 			this.normals = [];
 			this.uvs = [];
 
-			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
-			this.activeMtlName = Validator.verifyInput( activeMtlName, '' );
+			this.useIndices = useIndices === true;
+			this.disregardNormals = disregardNormals === true;
+
 			this.objectName = '';
 			this.groupName = '';
+			this.activeMtlName = '';
 			this.mtllibName = '';
+			this.reset( materialPerSmoothingGroup );
+		}
+
+		RawMesh.prototype.reset = function ( materialPerSmoothingGroup ) {
+			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
+			this.subGroups = [];
+			this.subGroupInUse = null;
 			this.smoothingGroup = {
 				splitMaterials: materialPerSmoothingGroup === true,
 				normalized: -1,
 				real: -1
 			};
-			this.useIndices = useIndices === true;
-			this.disregardNormals = disregardNormals === true;
-
-			this.mtlCount = 0;
-			this.smoothingGroupCount = 0;
-
-			this.subGroups = [];
-			this.subGroupInUse = null;
 			// this default index is required as it is possible to define faces without 'g' or 'usemtl'
 			this.pushSmoothingGroup( 1 );
 
 			this.doubleIndicesCount = 0;
 			this.faceCount = 0;
-		}
-
-		RawMesh.prototype.newInstanceResetOffsets = function () {
-			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );
-
-			// move indices forward
-			newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3;
-			newRawObject.globalUvOffset = this.globalUvOffset + this.uvs.length / 2;
-			newRawObject.globalNormalOffset = this.globalNormalOffset + this.normals.length / 3;
-
-			return newRawObject;
-		};
-
-		RawMesh.prototype.newInstanceKeepOffsets = function () {
-			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );
-			// keep objectName
-			newRawObject.pushObject( this.objectName );
-
-			// keep current buffers and indices forward
-			newRawObject.vertices = this.vertices;
-			newRawObject.colors = this.colors;
-			newRawObject.uvs = this.uvs;
-			newRawObject.normals = this.normals;
-			newRawObject.globalVertexOffset = this.globalVertexOffset;
-			newRawObject.globalUvOffset = this.globalUvOffset;
-			newRawObject.globalNormalOffset = this.globalNormalOffset;
-
-			return newRawObject;
+			this.mtlCount = 0;
+			this.smoothingGroupCount = 0;
 		};
 
-		RawMesh.prototype.pushVertex = function ( buffer ) {
+		RawMesh.prototype.pushVertex = function ( buffer, haveVertexColors ) {
 			this.vertices.push( parseFloat( buffer[ 1 ] ) );
 			this.vertices.push( parseFloat( buffer[ 2 ] ) );
 			this.vertices.push( parseFloat( buffer[ 3 ] ) );
-		};
+			if ( haveVertexColors ) {
 
-		RawMesh.prototype.pushVertexAndVertextColors = function ( buffer ) {
-			this.vertices.push( parseFloat( buffer[ 1 ] ) );
-			this.vertices.push( parseFloat( buffer[ 2 ] ) );
-			this.vertices.push( parseFloat( buffer[ 3 ] ) );
-			this.colors.push( parseFloat( buffer[ 4 ] ) );
-			this.colors.push( parseFloat( buffer[ 5 ] ) );
-			this.colors.push( parseFloat( buffer[ 6 ] ) );
+				this.colors.push( parseFloat( buffer[ 4 ] ) );
+				this.colors.push( parseFloat( buffer[ 5 ] ) );
+				this.colors.push( parseFloat( buffer[ 6 ] ) );
+
+			}
 		};
 
 		RawMesh.prototype.pushUv = function ( buffer ) {
@@ -972,6 +926,10 @@ THREE.OBJLoader2 = (function () {
 			this.normals.push( parseFloat( buffer[ 3 ] ) );
 		};
 
+		RawMesh.prototype.pushGroup = function ( groupName ) {
+			this.groupName = Validator.verifyInput( groupName, '' );
+		};
+
 		RawMesh.prototype.pushObject = function ( objectName ) {
 			this.objectName = Validator.verifyInput( objectName, '' );
 		};
@@ -980,10 +938,6 @@ THREE.OBJLoader2 = (function () {
 			this.mtllibName = Validator.verifyInput( mtllibName, '' );
 		};
 
-		RawMesh.prototype.pushGroup = function ( groupName ) {
-			this.groupName = Validator.verifyInput( groupName, '' );
-		};
-
 		RawMesh.prototype.pushUsemtl = function ( mtlName ) {
 			if ( this.activeMtlName === mtlName || ! Validator.isValid( mtlName ) ) return;
 			this.activeMtlName = mtlName;
@@ -1028,7 +982,7 @@ THREE.OBJLoader2 = (function () {
 			// "f vertex ..."
 			if ( slashesCount === 0 ) {
 
-				for ( i = 2, length = bufferLength - 1; i < length; i ++ ) {
+				for ( i = 2, length = bufferLength; i < length; i ++ ) {
 
 					this.buildFace( buffer[ 1 ] );
 					this.buildFace( buffer[ i ] );
@@ -1072,20 +1026,22 @@ THREE.OBJLoader2 = (function () {
 			}
 		};
 
+
 		RawMesh.prototype.buildFace = function ( faceIndexV, faceIndexU, faceIndexN ) {
 			var sgiu = this.subGroupInUse;
 			if ( this.disregardNormals ) faceIndexN = undefined;
 			var scope = this;
 			var updateRawObjectDescriptionInUse = function () {
 
-				var indexPointerV = ( parseInt( faceIndexV ) - scope.globalVertexOffset ) * 3;
-				var indexPointerC = scope.colors.length > 0 ? indexPointerV : null;
+				var faceIndexVi = parseInt( faceIndexV );
+				var indexPointerV = 3 * ( faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3 );
 
 				var vertices = sgiu.vertices;
 				vertices.push( scope.vertices[ indexPointerV++ ] );
 				vertices.push( scope.vertices[ indexPointerV++ ] );
 				vertices.push( scope.vertices[ indexPointerV ] );
 
+				var indexPointerC = scope.colors.length > 0 ? indexPointerV : null;
 				if ( indexPointerC !== null ) {
 
 					var colors = sgiu.colors;
@@ -1097,7 +1053,8 @@ THREE.OBJLoader2 = (function () {
 
 				if ( faceIndexU ) {
 
-					var indexPointerU = ( parseInt( faceIndexU ) - scope.globalUvOffset ) * 2;
+					var faceIndexUi = parseInt( faceIndexU );
+					var indexPointerU = 2 * ( faceIndexUi > 0 ? faceIndexUi - 1 : faceIndexUi + scope.uvs.length / 2 );
 					var uvs = sgiu.uvs;
 					uvs.push( scope.uvs[ indexPointerU++ ] );
 					uvs.push( scope.uvs[ indexPointerU ] );
@@ -1105,7 +1062,8 @@ THREE.OBJLoader2 = (function () {
 				}
 				if ( faceIndexN ) {
 
-					var indexPointerN = ( parseInt( faceIndexN ) - scope.globalNormalOffset ) * 3;
+					var faceIndexNi = parseInt( faceIndexN );
+					var indexPointerN = 3 * ( faceIndexNi > 0 ? faceIndexNi - 1 : faceIndexNi + scope.normals.length / 3 );
 					var normals = sgiu.normals;
 					normals.push( scope.normals[ indexPointerN++ ] );
 					normals.push( scope.normals[ indexPointerN++ ] );
@@ -1339,12 +1297,11 @@ THREE.OBJLoader2 = (function () {
 	 * @memberOf THREE.OBJLoader2
 	 *
 	 * @param {string} url URL to the file
-	 * @param {string} name The name of the object
 	 * @param {Object} content The file content as arraybuffer or text
 	 * @param {function} callbackOnLoad
 	 * @param {string} [crossOrigin] CORS value
 	 */
-	OBJLoader2.prototype.loadMtl = function ( url, name, content, callbackOnLoad, crossOrigin ) {
+	OBJLoader2.prototype.loadMtl = function ( url, content, callbackOnLoad, crossOrigin ) {
 		var resource = new THREE.LoaderSupport.ResourceDescriptor( url, 'MTL' );
 		resource.setContent( content );
 		this._loadMtl( resource, callbackOnLoad, crossOrigin );
@@ -1359,6 +1316,7 @@ THREE.OBJLoader2 = (function () {
 	 * @param {string} [crossOrigin] CORS value
 	 */
 	OBJLoader2.prototype._loadMtl = function ( resource, callbackOnLoad, crossOrigin ) {
+		if ( THREE.MTLLoader === undefined ) console.error( '"THREE.MTLLoader" is not available. "THREE.OBJLoader2" requires it for loading MTL files.' );
 		if ( Validator.isValid( resource ) ) this.logger.logTimeStart( 'Loading MTL: ' + resource.name );
 
 		var materials = [];
@@ -1403,7 +1361,7 @@ THREE.OBJLoader2 = (function () {
 
 				var onError = function ( event ) {
 					var output = 'Error occurred while downloading "' + resource.url + '"';
-					this.logger.logError( output + ': ' + event );
+					scope.logger.logError( output + ': ' + event );
 					throw output;
 				};
 

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