Mr.doob 6 years ago
parent
commit
c71d1206e8
100 changed files with 3710 additions and 1429 deletions
  1. 30 43
      build/three.js
  2. 295 409
      build/three.min.js
  3. 30 43
      build/three.module.js
  4. 1 1
      docs/api/en/core/Object3D.html
  5. 2 0
      docs/api/en/geometries/ExtrudeBufferGeometry.html
  6. 2 0
      docs/api/en/geometries/ExtrudeGeometry.html
  7. 2 0
      docs/api/en/geometries/TextBufferGeometry.html
  8. 2 0
      docs/api/en/geometries/TextGeometry.html
  9. 131 0
      docs/api/en/materials/MeshDistanceMaterial.html
  10. 150 0
      docs/api/en/materials/MeshMatcapMaterial.html
  11. 61 1
      docs/api/en/materials/MeshNormalMaterial.html
  12. 2 2
      docs/api/en/materials/MeshToonMaterial.html
  13. 1 1
      docs/api/en/objects/LOD.html
  14. 12 3
      docs/api/en/renderers/WebGLRenderer.html
  15. 8 3
      docs/api/en/textures/Texture.html
  16. 1 1
      docs/api/zh/core/Object3D.html
  17. 112 0
      docs/api/zh/materials/MeshDistanceMaterial.html
  18. 120 0
      docs/api/zh/materials/MeshMatcapMaterial.html
  19. 42 1
      docs/api/zh/materials/MeshNormalMaterial.html
  20. 2 2
      docs/api/zh/materials/MeshToonMaterial.html
  21. 1 1
      docs/api/zh/objects/LOD.html
  22. 9 0
      docs/api/zh/renderers/WebGLRenderer.html
  23. 4 0
      docs/api/zh/textures/Texture.html
  24. 78 62
      docs/examples/quickhull/QuickHull.html
  25. 1 0
      docs/index.html
  26. 4 0
      docs/list.js
  27. 8 1
      docs/manual/en/introduction/How-to-dispose-of-objects.html
  28. 65 4
      docs/manual/en/introduction/Import-via-modules.html
  29. 14 4
      docs/scenes/js/geometry.js
  30. 171 49
      docs/scenes/js/material.js
  31. 4 0
      editor/index.html
  32. 7 1
      editor/js/Editor.js
  33. 2 2
      editor/js/History.js
  34. 63 0
      editor/js/Menubar.Add.js
  35. 62 0
      editor/js/Menubar.Edit.js
  36. 1 4
      editor/js/Menubar.File.js
  37. 22 19
      editor/js/Script.js
  38. 54 0
      editor/js/Sidebar.Geometry.OctahedronGeometry.js
  39. 95 0
      editor/js/Sidebar.Geometry.RingGeometry.js
  40. 55 0
      editor/js/Sidebar.Geometry.TetrahedronGeometry.js
  41. 2 0
      editor/js/Sidebar.Geometry.js
  42. 161 6
      editor/js/Sidebar.Material.js
  43. 112 6
      editor/js/Sidebar.Object.js
  44. 3 3
      editor/js/Sidebar.Settings.js
  45. 47 0
      editor/js/Strings.js
  46. 12 3
      editor/js/Viewport.js
  47. 1 1
      editor/js/libs/codemirror/mode/glsl.js
  48. 38 38
      editor/js/libs/three.html.js
  49. 20 3
      editor/js/libs/ui.three.js
  50. 5 3
      examples/files.js
  51. 1 0
      examples/index.html
  52. 102 0
      examples/js/QuickHull.js
  53. 25 22
      examples/js/animation/MMDPhysics.js
  54. 11 21
      examples/js/controls/PointerLockControls.js
  55. 8 0
      examples/js/controls/TransformControls.js
  56. 47 5
      examples/js/effects/OutlineEffect.js
  57. 32 10
      examples/js/exporters/GLTFExporter.js
  58. 7 3
      examples/js/libs/ammo.js
  59. 121 0
      examples/js/lights/LightProbeGenerator.js
  60. 37 32
      examples/js/loaders/GLTFLoader.js
  61. 5 3
      examples/js/loaders/KTXLoader.js
  62. 201 75
      examples/js/loaders/LDrawLoader.js
  63. 167 25
      examples/js/loaders/LWOLoader.js
  64. 3 0
      examples/js/loaders/MMDLoader.js
  65. 18 5
      examples/js/loaders/SVGLoader.js
  66. 21 5
      examples/js/loaders/VRMLLoader.js
  67. 59 355
      examples/js/math/Lut.js
  68. 12 3
      examples/js/nodes/accessors/NormalNode.js
  69. 20 4
      examples/js/nodes/accessors/PositionNode.js
  70. 7 4
      examples/js/nodes/accessors/ResolutionNode.js
  71. 2 1
      examples/js/renderers/CSS2DRenderer.js
  72. 8 8
      examples/js/shaders/DepthLimitedBlurShader.js
  73. 5 5
      examples/js/shaders/LuminosityHighPassShader.js
  74. 17 17
      examples/js/shaders/SAOShader.js
  75. 56 45
      examples/jsm/controls/OrbitControls.d.ts
  76. 7 0
      examples/jsm/exporters/ColladaExporter.d.ts
  77. 9 5
      examples/jsm/exporters/ColladaExporter.js
  78. 3 3
      examples/jsm/exporters/GLTFExporter.d.ts
  79. 32 10
      examples/jsm/exporters/GLTFExporter.js
  80. 7 0
      examples/jsm/exporters/MMDExporter.d.ts
  81. 7 0
      examples/jsm/exporters/OBJExporter.d.ts
  82. 7 0
      examples/jsm/exporters/PLYExporter.d.ts
  83. 7 0
      examples/jsm/exporters/STLExporter.d.ts
  84. 7 0
      examples/jsm/exporters/TypedGeometryExporter.d.ts
  85. 37 32
      examples/jsm/loaders/GLTFLoader.js
  86. 357 0
      examples/jsm/loaders/STLLoader.js
  87. 1 6
      examples/jsm/pmrem/PMREMCubeUVPacker.d.ts
  88. 5 0
      examples/jsm/utils/MathUtils.d.ts
  89. 8 0
      examples/jsm/utils/ShadowMapViewer.d.ts
  90. 32 0
      examples/jsm/utils/SkeletonUtils.d.ts
  91. 58 0
      examples/jsm/utils/TypedArrayUtils.d.ts
  92. 3 0
      examples/jsm/utils/UVsDebug.d.ts
  93. 1 2
      examples/misc_controls_pointerlock.html
  94. 3 3
      examples/misc_controls_transform.html
  95. 0 0
      examples/models/json/pressure.json
  96. BIN
      examples/models/sea3d/morph.sea
  97. BIN
      examples/models/sea3d/morph.tjs.sea
  98. BIN
      examples/textures/alphaMap.jpg
  99. BIN
      examples/textures/equirectangular/pedestrian_overpass_2k.hdr
  100. BIN
      examples/textures/equirectangular/venice_sunset_2k.hdr

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


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


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


+ 1 - 1
docs/api/en/core/Object3D.html

@@ -46,7 +46,7 @@
 
 		<h3>[property:Material customDistanceMaterial]</h3>
 		<p>
-		Same as customDepthMaterial, but used with [page:PointLight]. Default is *undefined*.
+		Same as [page:.customDepthMaterial customDepthMaterial], but used with [page:PointLight]. Default is *undefined*.
 		</p>
 
 		<h3>[property:Boolean frustumCulled]</h3>

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

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

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

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

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

@@ -55,6 +55,7 @@
 				bevelEnabled: true,
 				bevelThickness: 10,
 				bevelSize: 8,
+				bevelOffset: 0,
 				bevelSegments: 5
 			} );
 		} );
@@ -74,6 +75,7 @@
 			<li>bevelEnabled — Boolean. Turn on bevel. Default is False.</li>
 			<li>bevelThickness — Float. How deep into text bevel goes. Default is 10.</li>
 			<li>bevelSize — Float. How far from text outline is bevel. Default is 8.</li>
+			<li>bevelOffset — Float. How far from text outline bevel starts. Default is 0.</li>
 			<li>bevelSegments — Integer. Number of bevel segments. Default is 3.</li>
 		</ul>
 		</p>

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

@@ -55,6 +55,7 @@
 				bevelEnabled: true,
 				bevelThickness: 10,
 				bevelSize: 8,
+				bevelOffset: 0,
 				bevelSegments: 5
 			} );
 		} );
@@ -74,6 +75,7 @@
 			<li>bevelEnabled — Boolean. Turn on bevel. Default is False.</li>
 			<li>bevelThickness — Float. How deep into text bevel goes. Default is 10.</li>
 			<li>bevelSize — Float. How far from text outline is bevel. Default is 8.</li>
+			<li>bevelOffset — Float. How far from text outline bevel starts. Default is 0.</li>
 			<li>bevelSegments — Integer. Number of bevel segments. Default is 3.</li>
 		</ul>
 		</p>

+ 131 - 0
docs/api/en/materials/MeshDistanceMaterial.html

@@ -0,0 +1,131 @@
+<!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:Material] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			[name] is internally used for implementing shadow mapping with [page:PointLight]s.<br/><br/>
+
+			Can also be used to customize the shadow casting of an object by assigning an instance of [name] to [page:Object3D.customDistanceMaterial].
+			The following examples demonstrates this approach in order to ensure transparent parts of objects do no cast shadows.
+		</p>
+
+		<h2>Example</h2>
+
+		[example:webgl_shadowmap_pointlight WebGL / shadowmap / pointlight]
+
+		<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>Constructor</h2>
+
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>
+			[page:Object parameters] - (optional) an object with one or more properties defining the material's appearance.
+			Any property of the material (including any property inherited from [page:Material]) can be passed in here.
+		</p>
+
+		<h2>Properties</h2>
+		<p>See the base [page:Material] class for common properties.</p>
+
+		<h3>[property:Texture alphaMap]</h3>
+		<p>The alpha map is a grayscale texture that controls the opacity across the surface
+			(black: fully transparent; white: fully opaque). Default is null.<br /><br />
+
+			Only the color of the texture is used, ignoring the alpha channel if one exists.
+			For RGB and RGBA textures, the [page:WebGLRenderer WebGL] renderer will use the
+			green channel when sampling this texture due to the extra bit of precision provided
+			for green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and
+			luminance/alpha textures will also still work as expected.
+		</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p>
+			The displacement map affects the position of the mesh's vertices. Unlike other maps
+			which only affect the light and shade of the material the displaced vertices can cast shadows,
+			block other objects, and otherwise act as real geometry. The displacement texture is
+			an image where the value of each pixel (white being the highest) is mapped against,
+			and repositions, the vertices of the mesh.
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p>
+			How much the displacement map affects the mesh (where black is no displacement,
+			and white is maximum displacement). Without a displacement map set, this value is not applied.
+			 Default is 1.
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p>
+			The offset of the displacement map's values on the mesh's vertices.
+			Without a displacement map set, this value is not applied. Default is 0.
+		</p>
+
+		<h3>[property:Float farDistance]</h3>
+		<p>
+			The far value of the point light's internal shadow camera.
+		</p>
+
+		<h3>[property:Boolean fog]</h3>
+		<p>Whether the material is affected by fog. Default is *false*.</p>
+
+		<h3>[property:Boolean isMeshDistanceMaterial]</h3>
+		<p>
+			Used to check whether this or derived classes are mesh depth materials. Default is *true*.<br /><br />
+
+			You should not change this, as it used internally for optimisation.
+		</p>
+
+		<h3>[property:Boolean lights]</h3>
+		<p>Whether the material is affected by lights. Default is *false*.</p>
+
+		<h3>[property:Texture map]</h3>
+		<p>The color map. Default is  null.</p>
+
+		<h3>[property:boolean morphTargets]</h3>
+		<p>Define whether the material uses morphTargets. Default is false.</p>
+
+		<h3>[property:Float nearDistance]</h3>
+		<p>
+			The near value of the point light's internal shadow camera.
+		</p>
+
+		<h3>[property:Vector3 referencePosition]</h3>
+		<p>
+			The position of the point light in world space.
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>Define whether the material uses skinning. Default is false.</p>
+
+		<h2>Methods</h2>
+		<p>See the base [page:Material] class for common methods.</p>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 150 - 0
docs/api/en/materials/MeshMatcapMaterial.html

@@ -0,0 +1,150 @@
+<!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:Material] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			[name] is defined by a MatCap (or Lit Sphere) texture, which encodes the material color and shading.<br/><br/>
+			[name] does not respond to lights since the matcap image file encodes baked lighting.
+			It will cast a shadow onto an object that receives shadows (and shadow clipping works), but it will not self-shadow or receive shadows.
+		</p>
+
+		<iframe id="scene" src="scenes/material-browser.html#MeshMatcapMaterial"></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>Constructor</h2>
+
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>
+			[page:Object parameters] - (optional) an object with one or more properties defining the material's appearance.
+			Any property of the material (including any property inherited from [page:Material]) can be passed in here.<br /><br />
+
+			The exception is the property [page:Hexadecimal color], which can be passed in as a hexadecimal
+			string and is *0xffffff* (white) by default. [page:Color.set]( color ) is called internally.
+		</p>
+
+		<h2>Properties</h2>
+		<p>See the base [page:Material] class for common properties.</p>
+
+		<h3>[property:Texture alphaMap]</h3>
+		<p>The alpha map is a grayscale texture that controls the opacity across the surface
+			(black: fully transparent; white: fully opaque). Default is null.<br /><br />
+
+			Only the color of the texture is used, ignoring the alpha channel if one exists.
+			For RGB and RGBA textures, the [page:WebGLRenderer WebGL] renderer will use the
+			green channel when sampling this texture due to the extra bit of precision provided
+			for green in DXT-compressed and uncompressed RGB 565 formats. Luminance-only and
+			luminance/alpha textures will also still work as expected.
+		</p>
+
+		<h3>[property:Texture bumpMap]</h3>
+		<p>
+			The texture to create a bump map. The black and white values map to the perceived depth in relation to the lights.
+			Bump doesn't actually affect the geometry of the object, only the lighting. If a normal map is defined this will
+			be ignored.
+		</p>
+
+		<h3>[property:Float bumpScale]</h3>
+		<p>How much the bump map affects the material. Typical ranges are 0-1. Default is 1.</p>
+
+		<h3>[property:Color color]</h3>
+		<p>[page:Color] of the material, by default set to white (0xffffff).</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p>
+			The displacement map affects the position of the mesh's vertices. Unlike other maps
+			which only affect the light and shade of the material the displaced vertices can cast shadows,
+			block other objects, and otherwise act as real geometry. The displacement texture is
+			an image where the value of each pixel (white being the highest) is mapped against,
+			and repositions, the vertices of the mesh.
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p>
+			How much the displacement map affects the mesh (where black is no displacement,
+			and white is maximum displacement). Without a displacement map set, this value is not applied.
+			 Default is 1.
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p>
+			The offset of the displacement map's values on the mesh's vertices.
+			Without a displacement map set, this value is not applied. Default is 0.
+		</p>
+
+		<h3>[property:Boolean isMeshMatcapMaterial]</h3>
+		<p>
+			Used to check whether this or derived classes are mesh Matcap materials. Default is *true*.<br /><br />
+
+			You should not change this, as it used internally for optimisation.
+		</p>
+
+		<h3>[property:Texture map]</h3>
+		<p>The color map. Default is null. The texture map color is modulated by the diffuse [page:.color].</p>
+
+		<h3>[property:Texture matcap]</h3>
+		<p>The matcap map. Default is null.</p>
+
+		<h3>[property:boolean morphNormals]</h3>
+		<p>
+			Defines whether the material uses morphNormals. Set as true to pass morphNormal
+			attributes from the [page:Geometry]	to the shader. Default is *false*.
+		</p>
+
+		<h3>[property:Boolean morphTargets]</h3>
+		<p>Define whether the material uses morphTargets. Default is false.</p>
+
+		<h3>[property:Texture normalMap]</h3>
+		<p>
+			The texture to create a normal map. The RGB values affect the surface normal for each pixel fragment and change
+			the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
+		</p>
+
+		<h3>[property:Integer normalMapType]</h3>
+		<p>
+			The type of normal map.<br /><br />
+
+			Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+		</p>
+
+		<h3>[property:Vector2 normalScale]</h3>
+		<p>
+			How much the normal map affects the material. Typical ranges are 0-1.
+			Default is a [page:Vector2] set to (1,1).
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>Define whether the material uses skinning. Default is false.</p>
+
+		<h2>Methods</h2>
+		<p>See the base [page:Material] class for common methods.</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 61 - 1
docs/api/en/materials/MeshNormalMaterial.html

@@ -44,6 +44,38 @@
 		<h2>Properties</h2>
 		<p>See the base [page:Material] class for common properties.</p>
 
+		<h3>[property:Texture bumpMap]</h3>
+		<p>
+			The texture to create a bump map. The black and white values map to the perceived depth in relation to the lights.
+			Bump doesn't actually affect the geometry of the object, only the lighting. If a normal map is defined this will
+			be ignored.
+		</p>
+
+		<h3>[property:Float bumpScale]</h3>
+		<p>How much the bump map affects the material. Typical ranges are 0-1. Default is 1.</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p>
+			The displacement map affects the position of the mesh's vertices. Unlike other maps
+			which only affect the light and shade of the material the displaced vertices can cast shadows,
+			block other objects, and otherwise act as real geometry. The displacement texture is
+			an image where the value of each pixel (white being the highest) is mapped against,
+			and repositions, the vertices of the mesh.
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p>
+			How much the displacement map affects the mesh (where black is no displacement,
+			and white is maximum displacement). Without a displacement map set, this value is not applied.
+			 Default is 1.
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p>
+			The offset of the displacement map's values on the mesh's vertices.
+			Without a displacement map set, this value is not applied. Default is 0.
+		</p>
+
 		<h3>[property:Boolean fog]</h3>
 		<p>Whether the material is affected by fog. Default is *false*.</p>
 
@@ -57,9 +89,37 @@
 		<h3>[property:Boolean lights]</h3>
 		<p>Whether the material is affected by lights. Default is *false*.</p>
 
-		<h3>[property:boolean morphTargets]</h3>
+		<h3>[property:boolean morphNormals]</h3>
+		<p>
+			Defines whether the material uses morphNormals. Set as true to pass morphNormal
+			attributes from the [page:Geometry]	to the shader. Default is *false*.
+		</p>
+
+		<h3>[property:Boolean morphTargets]</h3>
 		<p>Define whether the material uses morphTargets. Default is false.</p>
 
+		<h3>[property:Texture normalMap]</h3>
+		<p>
+			The texture to create a normal map. The RGB values affect the surface normal for each pixel fragment and change
+			the way the color is lit. Normal maps do not change the actual shape of the surface, only the lighting.
+		</p>
+
+		<h3>[property:Integer normalMapType]</h3>
+		<p>
+			The type of normal map.<br /><br />
+
+			Options are [page:constant THREE.TangentSpaceNormalMap] (default), and [page:constant THREE.ObjectSpaceNormalMap].
+		</p>
+
+		<h3>[property:Vector2 normalScale]</h3>
+		<p>
+			How much the normal map affects the material. Typical ranges are 0-1.
+			Default is a [page:Vector2] set to (1,1).
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>Define whether the material uses skinning. Default is false.</p>
+
 		<h3>[property:boolean wireframe]</h3>
 		<p>
 			Render geometry as wireframe. Default is false (i.e. render as smooth shaded).

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

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

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

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

+ 12 - 3
docs/api/en/renderers/WebGLRenderer.html

@@ -92,6 +92,15 @@
 			Default is *true*.
 		</p>
 
+		<h3>[property:Boolean debug.checkShaderErrors]</h3>
+		<p>
+			If [page:.checkShaderErrors checkShaderErrors] is true, defines whether material shader programs are checked
+			for errors during compilation and linkage process. It may be useful to disable this check in production for performance gain.
+			It is strongly recommended to keep these checks enabled during development.
+			If the shader does not compile and link - it will not work and associated material will not render.
+			Default is *true*.
+		</p>
+
 		<h3>[property:Object capabilities]</h3>
 		<p>
 		An object containing details about the capabilities of the current [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext RenderingContext].<br />
@@ -339,7 +348,7 @@
 		<p>Returns an object that describes the attributes set on the WebGL context when it was created.</p>
 
 		<h3>[method:RenderTarget getRenderTarget]()</h3>
-		<p>Returns the current RenderTarget, if any.</p>
+		<p>Returns the current [page:RenderTarget RenderTarget] if there are; returns *null* otherwise.</p>
 
 		<h3>[method:Vector4 getCurrentViewport]( [param:Vector4 target] )</h3>
 		<p>
@@ -429,10 +438,10 @@
 
 		<h3>[method:null setRenderTarget]( [param:WebGLRenderTarget renderTarget], [param:Integer activeCubeFace], [param:Integer activeMipMapLevel] )</h3>
 		<p>
-		renderTarget -- The [page:WebGLRenderTarget renderTarget] that needs to be activated (optional).<br />
+		renderTarget -- The [page:WebGLRenderTarget renderTarget] that needs to be activated. When *null* is given, the canvas is set as the active render target instead.<br />
 		activeCubeFace -- Specifies the active cube side (PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5) of [page:WebGLRenderTargetCube] (optional).<br />
 		activeMipMapLevel -- Specifies the active mipmap level (optional).<br /><br />
-		This method sets the active rendertarget. If the parameter is omitted the canvas is set as the active rendertarget.
+		This method sets the active rendertarget.
 		</p>
 
 		<h3>[method:null setScissor]( [param:Integer x], [param:Integer y], [param:Integer width], [param:Integer height] )<br />

+ 8 - 3
docs/api/en/textures/Texture.html

@@ -187,13 +187,18 @@
 
 		<h3>[property:boolean premultiplyAlpha]</h3>
 		<p>
-		False by default, which is the norm for PNG images. Set to true if the RGB values have
-		been stored premultiplied by alpha.
+		If set to *true*, the alpha channel, if present, is multiplied into the color channels when the texture is uploaded to the GPU. Defaut is *false*.<br /><br />
+
+		Note that this property has no effect for [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap].
+		You need to configure on bitmap creation instead. See [page:ImageBitmapLoader].
 		</p>
 
 		<h3>[property:boolean flipY]</h3>
 		<p>
-		True by default. Flips the image's Y axis to match the WebGL texture coordinate space.
+		If set to *true*, the texture is flipped along the vertical axis when uploaded to the GPU. Default is *true*.<br /><br />
+
+		Note that this property has no effect for [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap].
+		You need to configure on bitmap creation instead. See [page:ImageBitmapLoader].
 		</p>
 
 		<h3>[property:number unpackAlignment]</h3>

+ 1 - 1
docs/api/zh/core/Object3D.html

@@ -44,7 +44,7 @@
 	</p>
 
 	<h3>[property:Material customDistanceMaterial]</h3>
-	<p>与customDepthMaterial相同,但与[page:PointLight]一起使用。默认值为*undefined*。
+	<p>与[page:.customDepthMaterial customDepthMaterial]相同,但与[page:PointLight]一起使用。默认值为*undefined*。
 	</p>
 
 	<h3>[property:Boolean frustumCulled]</h3>

+ 112 - 0
docs/api/zh/materials/MeshDistanceMaterial.html

@@ -0,0 +1,112 @@
+<!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:Material] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			TODO
+		</p>
+
+		<h2>Example</h2>
+
+		[example:webgl_shadowmap_pointlight WebGL / shadowmap / pointlight]
+
+		<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>构造函数(Constructor)</h2>
+
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>[page:Object parameters] - (可选)用于定义材质外观的对象,具有一个或多个属性。
+			材质的任何属性都可以从此处传入(包括从[page:Material]继承的任何属性)。
+		</p>
+
+		<h2>属性(Properties)</h2>
+		<p>常用属性请参见基类[page:Material]。</p>
+
+		<h3>[property:Texture alphaMap]</h3>
+		<p>alpha贴图是一种灰度纹理,用于控制整个表面的不透明度(黑色:完全透明;白色:完全不透明)。默认值为null。<br /><br />
+			仅使用纹理的颜色,忽略alpha通道(如果存在)。对于RGB和RGBA纹理,[page:WebGLRenderer WebGL]渲染器在采样此纹理时将使用绿色通道,
+			因为在DXT压缩和未压缩RGB 565格式中为绿色提供了额外的精度。Luminance-only以及luminance/alpha纹理也仍然有效。
+		</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p> 位移贴图会影响网格顶点的位置,与仅影响材质的光照和阴影的其他贴图不同,移位的顶点可以投射阴影,阻挡其他对象,以及充当真实的几何体。
+			位移纹理是指:网格的所有顶点被映射为图像中每个像素的值(白色是最高的),并且被重定位。
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p>位移贴图对网格的影响程度(黑色是无位移,白色是最大位移)。如果没有设置位移贴图,则不会应用此值。默认值为1。
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p> 位移贴图在网格顶点上的偏移量。如果没有设置位移贴图,则不会应用此值。默认值为0。
+		</p>
+
+		<h3>[property:Float farDistance]</h3>
+		<p>
+			TODO
+		</p>
+
+		<h3>[property:Boolean fog]</h3>
+		<p> 材质是否受雾影响。默认值为*false*。</p>
+
+		<h3>[property:Boolean isMeshDistanceMaterial]</h3>
+		<p> 用于检查此类或派生类是否为深度网格材质。默认值为 *true*。<br /><br />
+
+			因为其通常用在内部优化,所以不应该更改该属性值。
+		</p>
+
+		<h3>[property:Boolean lights]</h3>
+		<p>材质是否受到光照的影响。默认值为 *false*。</p>
+
+		<h3>[property:Texture map]</h3>
+		<p>颜色贴图。默认为null。</p>
+
+		<h3>[property:boolean morphTargets]</h3>
+		<p>材质是否使用morphTargets。默认值为false。</p>
+
+		<h3>[property:Float nearDistance]</h3>
+		<p>
+			TODO
+		</p>
+
+		<h3>[property:Vector3 referencePosition]</h3>
+		<p>
+			TODO
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>材质是否使用蒙皮。默认值为false。</p>
+
+		<h2>方法(Methods)</h2>
+		<p>常用方法请参见基类[page:Material]。</p>
+
+
+		<h2>源码(Source)</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 120 - 0
docs/api/zh/materials/MeshMatcapMaterial.html

@@ -0,0 +1,120 @@
+<!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:Material] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			TODO
+		</p>
+
+		<iframe id="scene" src="scenes/material-browser.html#MeshMatcapMaterial"></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>构造函数(Constructor)</h2>
+
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>[page:Object parameters] - (可选)用于定义材质外观的对象,具有一个或多个属性。
+			材质的任何属性都可以从此处传入(包括从[page:Material]继承的任何属性)。<br /><br />
+			属性[page:Hexadecimal color]例外,其可以作为十六进制字符串传递,默认情况下为 *0xffffff*(白色),内部调用[page:Color.set](color)。
+		</p>
+
+		<h2>属性(Properties)</h2>
+		<p>常用属性请参见基类[page:Material]。</p>
+
+		<h3>[property:Texture alphaMap]</h3>
+		<p>Talpha贴图是一种灰度纹理,用于控制整个表面的不透明度(黑色:完全透明;白色:完全不透明)。默认值为null。<br /><br />
+			仅使用纹理的颜色,忽略alpha通道(如果存在)。对于RGB和RGBA纹理,[page:WebGLRenderer WebGL]渲染器在采样此纹理时将使用绿色通道,
+			因为在DXT压缩和未压缩RGB 565格式中为绿色提供了额外的精度。Luminance-only以及luminance/alpha纹理也仍然有效。
+		</p>
+
+		<h3>[property:Texture bumpMap]</h3>
+		<p> 用于创建凹凸贴图的纹理。黑色和白色值映射到与光照相关的感知深度。凹凸实际上不会影响对象的几何形状,只影响光照。如果定义了法线贴图,则将忽略该贴图。
+		</p>
+
+		<h3>[property:Float bumpScale]</h3>
+		<p> 凹凸贴图会对材质产生多大影响。典型范围是0-1。默认值为1。</p>
+
+		<h3>[property:Color color]</h3>
+		<p>材质的颜色([page:Color]),默认值为白色 (0xffffff)。</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p> 位移贴图会影响网格顶点的位置,与仅影响材质的光照和阴影的其他贴图不同,移位的顶点可以投射阴影,阻挡其他对象,
+			以及充当真实的几何体。位移纹理是指:网格的所有顶点被映射为图像中每个像素的值(白色是最高的),并且被重定位。
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p> 位移贴图对网格的影响程度(黑色是无位移,白色是最大位移)。如果没有设置位移贴图,则不会应用此值。默认值为1。
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p>
+			位移贴图在网格顶点上的偏移量。如果没有设置位移贴图,则不会应用此值。默认值为0。
+		</p>
+
+		<h3>[property:Boolean isMeshMatcapMaterial]</h3>
+		<p>TODO<br /><br />
+
+			因为其通常用在内部优化,所以不应该更改该属性值。
+		</p>
+
+		<h3>[property:Texture map]</h3>
+		<p>颜色贴图。默认为null。纹理贴图颜色由漫反射颜色[page:.color]调节。</p>
+
+		<h3>[property:Texture matcap]</h3>
+		<p>TODO</p>
+
+		<h3>[property:boolean morphNormals]</h3>
+		<p> 定义是否使用morphNormals。设置为true可将morphNormal属性从[page:Geometry]传递到shader。默认值为*false*。
+		</p>
+
+		<h3>[property:Boolean morphTargets]</h3>
+		<p>定义材质是否使用morphTargets。默认值为false。</p>
+
+		<h3>[property:Texture normalMap]</h3>
+		<p> 用于创建法线贴图的纹理。RGB值会影响每个像素片段的曲面法线,并更改颜色照亮的方式。法线贴图不会改变曲面的实际形状,只会改变光照。
+		</p>
+
+		<h3>[property:Integer normalMapType]</h3>
+		<p> 法线贴图的类型。<br /><br />
+			选项为[page:constant THREE.TangentSpaceNormalMap](默认)和[page:constant THREE.ObjectSpaceNormalMap]。
+		</p>
+
+		<h3>[property:Vector2 normalScale]</h3>
+		<p> 法线贴图对材质的影响程度。典型范围是0-1。默认值是[page:Vector2]设置为(1,1)。
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>材质是否使用蒙皮。默认值为false。</p>
+
+		<h2>方法(Methods)</h2>
+		<p>常用方法请参见基类[page:Material]。</p>
+
+		<h2>源码(Source)</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 42 - 1
docs/api/zh/materials/MeshNormalMaterial.html

@@ -42,6 +42,27 @@
 		<h2>属性(Properties)</h2>
 		<p>常用属性请参见基类[page:Material]。</p>
 
+		<h3>[property:Texture bumpMap]</h3>
+		<p> 用于创建凹凸贴图的纹理。黑色和白色值映射到与光照相关的感知深度。凹凸实际上不会影响对象的几何形状,只影响光照。如果定义了法线贴图,则将忽略该贴图。
+		</p>
+
+		<h3>[property:Float bumpScale]</h3>
+		<p> 凹凸贴图会对材质产生多大影响。典型范围是0-1。默认值为1。</p>
+
+		<h3>[property:Texture displacementMap]</h3>
+		<p> 位移贴图会影响网格顶点的位置,与仅影响材质的光照和阴影的其他贴图不同,移位的顶点可以投射阴影,阻挡其他对象,
+			以及充当真实的几何体。位移纹理是指:网格的所有顶点被映射为图像中每个像素的值(白色是最高的),并且被重定位。
+		</p>
+
+		<h3>[property:Float displacementScale]</h3>
+		<p> 位移贴图对网格的影响程度(黑色是无位移,白色是最大位移)。如果没有设置位移贴图,则不会应用此值。默认值为1。
+		</p>
+
+		<h3>[property:Float displacementBias]</h3>
+		<p>
+			位移贴图在网格顶点上的偏移量。如果没有设置位移贴图,则不会应用此值。默认值为0。
+		</p>
+
 		<h3>[property:Boolean fog]</h3>
 		<p>材质是否受雾影响。默认值为*false*。</p>
 
@@ -54,9 +75,29 @@
 		<h3>[property:Boolean lights]</h3>
 		<p>材质是否受到光照的影响。默认值为 *false*。</p>
 
-		<h3>[property:boolean morphTargets]</h3>
+		<h3>[property:boolean morphNormals]</h3>
+		<p> 定义是否使用morphNormals。设置为true可将morphNormal属性从[page:Geometry]传递到shader。默认值为*false*。
+		</p>
+
+		<h3>[property:Boolean morphTargets]</h3>
 		<p>定义材质是否使用morphTargets。默认值为false。</p>
 
+		<h3>[property:Texture normalMap]</h3>
+		<p> 用于创建法线贴图的纹理。RGB值会影响每个像素片段的曲面法线,并更改颜色照亮的方式。法线贴图不会改变曲面的实际形状,只会改变光照。
+		</p>
+
+		<h3>[property:Integer normalMapType]</h3>
+		<p> 法线贴图的类型。<br /><br />
+			选项为[page:constant THREE.TangentSpaceNormalMap](默认)和[page:constant THREE.ObjectSpaceNormalMap]。
+		</p>
+
+		<h3>[property:Vector2 normalScale]</h3>
+		<p> 法线贴图对材质的影响程度。典型范围是0-1。默认值是[page:Vector2]设置为(1,1)。
+		</p>
+
+		<h3>[property:Boolean skinning]</h3>
+		<p>材质是否使用蒙皮。默认值为false。</p>
+
 		<h3>[property:boolean wireframe]</h3>
 		<p>
 			将几何体渲染为线框。默认值为*false*(即渲染为平滑着色)。

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

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

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

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

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

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

+ 4 - 0
docs/api/zh/textures/Texture.html

@@ -188,11 +188,15 @@
 		<p>
 			默认为false,这是PNG图像的规范。
 			如果RGB值已被Alpha预乘,请将其设为true。
+			Note that this property has no effect for [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap].
+			You need to configure on bitmap creation instead. See [page:ImageBitmapLoader].
 		</p>
 
 		<h3>[property:boolean flipY]</h3>
 		<p>
 		默认为true。翻转图像的Y轴以匹配WebGL纹理坐标空间。
+		Note that this property has no effect for [link:https://developer.mozilla.org/de/docs/Web/API/ImageBitmap ImageBitmap].
+		You need to configure on bitmap creation instead. See [page:ImageBitmapLoader].
 		</p>
 
 		<h3>[property:number unpackAlignment]</h3>

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

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

+ 1 - 0
docs/index.html

@@ -5,6 +5,7 @@
 		<title>three.js / documentation</title>
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link type="text/css" rel="stylesheet" href="index.css">
+		<link rel="shortcut icon" href="../favicon.ico" />
 	</head>
 	<body>
 		<script type="text/javascript">

+ 4 - 0
docs/list.js

@@ -253,7 +253,9 @@ var list = {
 				"Material": "api/en/materials/Material",
 				"MeshBasicMaterial": "api/en/materials/MeshBasicMaterial",
 				"MeshDepthMaterial": "api/en/materials/MeshDepthMaterial",
+				"MeshDistanceMaterial": "api/en/materials/MeshDistanceMaterial",
 				"MeshLambertMaterial": "api/en/materials/MeshLambertMaterial",
+				"MeshMatcapMaterial": "api/en/materials/MeshMatcapMaterial",
 				"MeshNormalMaterial": "api/en/materials/MeshNormalMaterial",
 				"MeshPhongMaterial": "api/en/materials/MeshPhongMaterial",
 				"MeshPhysicalMaterial": "api/en/materials/MeshPhysicalMaterial",
@@ -683,7 +685,9 @@ var list = {
 				"Material": "api/zh/materials/Material",
 				"MeshBasicMaterial": "api/zh/materials/MeshBasicMaterial",
 				"MeshDepthMaterial": "api/zh/materials/MeshDepthMaterial",
+				"MeshDistanceMaterial": "api/zh/materials/MeshDistanceMaterial",
 				"MeshLambertMaterial": "api/zh/materials/MeshLambertMaterial",
+				"MeshMatcapMaterial": "api/zh/materials/MeshMatcapMaterial",
 				"MeshNormalMaterial": "api/zh/materials/MeshNormalMaterial",
 				"MeshPhongMaterial": "api/zh/materials/MeshPhongMaterial",
 				"MeshPhysicalMaterial": "api/zh/materials/MeshPhysicalMaterial",

+ 8 - 1
docs/manual/en/introduction/How-to-dispose-of-objects.html

@@ -54,6 +54,13 @@
 		for realizing custom rendering destinations. These objects are only deallocated by executing [page:WebGLRenderTarget.dispose]().
 	</p>
 
+	<h2>Scenes</h2>
+
+	<p>
+		The renderer maintains for scenes special data structures for sorting and rendering. If for some reasons a scene object becomes obsolete in an application,
+		call [page:Scene.dispose]() in order to free these resources.
+	</p>
+
 	<h2>Miscellaneous</h2>
 
 	<p>
@@ -91,7 +98,7 @@
 		nothing happens. No resources were allocated so there is also no need for clean up.
 	</p>
 
-	<h3>Why happens when you call *dispose()* on a texture but the image is not loaded yet?</h3>
+	<h3>What happens when you call *dispose()* on a texture but the image is not loaded yet?</h3>
 
 	<p>
 		Internal resources for a texture are only allocated if the image has fully loaded. If you dispose a texture before the image was loaded,

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

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

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

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

+ 171 - 49
docs/scenes/js/material.js

@@ -87,6 +87,9 @@ function getObjectsKeys( obj ) {
 
 }
 
+var textureLoader = new THREE.TextureLoader();
+var cubeTextureLoader = new THREE.CubeTextureLoader();
+
 var envMaps = ( function () {
 
 	var path = '../../examples/textures/cube/SwedishRoyalCastle/';
@@ -97,10 +100,10 @@ var envMaps = ( function () {
 		path + 'pz' + format, path + 'nz' + format
 	];
 
-	var reflectionCube = new THREE.CubeTextureLoader().load( urls );
+	var reflectionCube = cubeTextureLoader.load( urls );
 	reflectionCube.format = THREE.RGBFormat;
 
-	var refractionCube = new THREE.CubeTextureLoader().load( urls );
+	var refractionCube = cubeTextureLoader.load( urls );
 	refractionCube.mapping = THREE.CubeRefractionMapping;
 	refractionCube.format = THREE.RGBFormat;
 
@@ -112,18 +115,81 @@ var envMaps = ( function () {
 
 } )();
 
-var envMapKeys = getObjectsKeys( envMaps );
+var diffuseMaps = ( function () {
+
+	var bricks = textureLoader.load( '../../examples/textures/brick_diffuse.jpg' );
+	bricks.wrapS = THREE.RepeatWrapping;
+	bricks.wrapT = THREE.RepeatWrapping;
+	bricks.repeat.set( 9, 1 );
+
+	return {
+		none: null,
+		bricks: bricks
+	};
+
+} )();
+
+var roughnessMaps = ( function () {
+
+	var bricks = textureLoader.load( '../../examples/textures/brick_roughness.jpg' );
+	bricks.wrapT = THREE.RepeatWrapping;
+	bricks.wrapS = THREE.RepeatWrapping;
+	bricks.repeat.set( 9, 1 );
+
+	return {
+		none: null,
+		bricks: bricks
+	};
+
+} )();
+
+var matcaps = ( function () {
+
+	return {
+		none: null,
+		porcelainWhite: textureLoader.load( '../../examples/textures/matcaps/matcap-porcelain-white.jpg' )
+	};
+
+} )();
 
-var textureMaps = ( function () {
+var alphaMaps = ( function () {
+
+	var fibers = textureLoader.load( '../../examples/textures/alphaMap.jpg' );
+	fibers.wrapT = THREE.RepeatWrapping;
+	fibers.wrapS = THREE.RepeatWrapping;
+	fibers.repeat.set( 9, 1 );
 
 	return {
 		none: null,
-		grass: new THREE.TextureLoader().load( '../../examples/textures/terrain/grasslight-thin.jpg' )
+		fibers: fibers
 	};
 
 } )();
 
-var textureMapKeys = getObjectsKeys( textureMaps );
+var gradientMaps = ( function () {
+
+	var threeTone = textureLoader.load( '../../examples/textures/gradientMaps/threeTone.jpg' );
+	threeTone.minFilter = THREE.NearestFilter;
+	threeTone.magFilter = THREE.NearestFilter;
+
+	var fiveTone = textureLoader.load( '../../examples/textures/gradientMaps/fiveTone.jpg' );
+	fiveTone.minFilter = THREE.NearestFilter;
+	fiveTone.magFilter = THREE.NearestFilter;
+
+	return {
+		none: null,
+		threeTone: threeTone,
+		fiveTone: fiveTone
+	};
+
+} )();
+
+var envMapKeys = getObjectsKeys( envMaps );
+var diffuseMapKeys = getObjectsKeys( diffuseMaps );
+var roughnessMapKeys = getObjectsKeys( roughnessMaps );
+var matcapKeys = getObjectsKeys( matcaps );
+var alphaMapKeys = getObjectsKeys( alphaMaps );
+var gradientMapKeys = getObjectsKeys( gradientMaps );
 
 function generateVertexColors( geometry ) {
 
@@ -247,7 +313,7 @@ function guiMaterial( gui, mesh, material, geometry ) {
 	var folder = gui.addFolder( 'THREE.Material' );
 
 	folder.add( material, 'transparent' );
-	folder.add( material, 'opacity', 0, 1 );
+	folder.add( material, 'opacity', 0, 1 ).step( 0.01 );
 	// folder.add( material, 'blending', constants.blendingMode );
 	// folder.add( material, 'blendSrc', constants.destinationFactors );
 	// folder.add( material, 'blendDst', constants.destinationFactors );
@@ -257,7 +323,7 @@ function guiMaterial( gui, mesh, material, geometry ) {
 	// folder.add( material, 'polygonOffset' );
 	// folder.add( material, 'polygonOffsetFactor' );
 	// folder.add( material, 'polygonOffsetUnits' );
-	folder.add( material, 'alphaTest', 0, 1 );
+	folder.add( material, 'alphaTest', 0, 1 ).step( 0.01 ).onChange( needsUpdate( material, geometry ) );
 	folder.add( material, 'visible' );
 	folder.add( material, 'side', constants.side ).onChange( needsUpdate( material, geometry ) );
 
@@ -267,10 +333,9 @@ function guiMeshBasicMaterial( gui, mesh, material, geometry ) {
 
 	var data = {
 		color: material.color.getHex(),
-		envMaps: envMapKeys,
-		map: textureMapKeys,
-		specularMap: textureMapKeys,
-		alphaMap: textureMapKeys
+		envMaps: envMapKeys[ 0 ],
+		map: diffuseMapKeys[ 0 ],
+		alphaMap: alphaMapKeys[ 0 ]
 	};
 
 	var folder = gui.addFolder( 'THREE.MeshBasicMaterial' );
@@ -282,9 +347,8 @@ function guiMeshBasicMaterial( gui, mesh, material, geometry ) {
 	folder.add( material, 'fog' );
 
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
-	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'specularMap', textureMapKeys ).onChange( updateTexture( material, 'specularMap', textureMaps ) );
-	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
 	folder.add( material, 'combine', constants.combine );
 	folder.add( material, 'reflectivity', 0, 1 );
 	folder.add( material, 'refractionRatio', 0, 1 );
@@ -293,11 +357,17 @@ function guiMeshBasicMaterial( gui, mesh, material, geometry ) {
 
 function guiMeshDepthMaterial( gui, mesh, material, geometry ) {
 
+	var data = {
+		alphaMap: alphaMapKeys[ 0 ]
+	};
+
 	var folder = gui.addFolder( 'THREE.MeshDepthMaterial' );
 
 	folder.add( material, 'wireframe' );
 	folder.add( material, 'wireframeLinewidth', 0, 10 );
 
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
+
 }
 
 function guiMeshNormalMaterial( gui, mesh, material, geometry ) {
@@ -332,10 +402,9 @@ function guiMeshLambertMaterial( gui, mesh, material, geometry ) {
 	var data = {
 		color: material.color.getHex(),
 		emissive: material.emissive.getHex(),
-		envMaps: envMapKeys,
-		map: textureMapKeys,
-		specularMap: textureMapKeys,
-		alphaMap: textureMapKeys
+		envMaps: envMapKeys[ 0 ],
+		map: diffuseMapKeys[ 0 ],
+		alphaMap: alphaMapKeys[ 0 ]
 	};
 
 	var folder = gui.addFolder( 'THREE.MeshLambertMaterial' );
@@ -349,26 +418,39 @@ function guiMeshLambertMaterial( gui, mesh, material, geometry ) {
 	folder.add( material, 'fog' );
 
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
-	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'specularMap', textureMapKeys ).onChange( updateTexture( material, 'specularMap', textureMaps ) );
-	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
 	folder.add( material, 'combine', constants.combine );
 	folder.add( material, 'reflectivity', 0, 1 );
 	folder.add( material, 'refractionRatio', 0, 1 );
 
 }
 
+function guiMeshMatcapMaterial( gui, mesh, material ) {
+
+	var data = {
+		color: material.color.getHex(),
+		matcap: matcapKeys[ 1 ],
+		alphaMap: alphaMapKeys[ 0 ]
+	};
+
+	var folder = gui.addFolder( 'THREE.MeshMatcapMaterial' );
+
+	folder.addColor( data, 'color' ).onChange( handleColorChange( material.color ) );
+	folder.add( data, 'matcap', matcapKeys ).onChange( updateTexture( material, 'matcap', matcaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
+
+}
+
 function guiMeshPhongMaterial( gui, mesh, material, geometry ) {
 
 	var data = {
 		color: material.color.getHex(),
 		emissive: material.emissive.getHex(),
 		specular: material.specular.getHex(),
-		envMaps: envMapKeys,
-		map: textureMapKeys,
-		lightMap: textureMapKeys,
-		specularMap: textureMapKeys,
-		alphaMap: textureMapKeys
+		envMaps: envMapKeys[ 0 ],
+		map: diffuseMapKeys[ 0 ],
+		alphaMap: alphaMapKeys[ 0 ]
 	};
 
 	var folder = gui.addFolder( 'THREE.MeshPhongMaterial' );
@@ -384,10 +466,27 @@ function guiMeshPhongMaterial( gui, mesh, material, geometry ) {
 	folder.add( material, 'vertexColors', constants.colors ).onChange( needsUpdate( material, geometry ) );
 	folder.add( material, 'fog' );
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
-	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( material, 'lightMap', textureMaps ) );
-	folder.add( data, 'specularMap', textureMapKeys ).onChange( updateTexture( material, 'specularMap', textureMaps ) );
-	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
+
+}
+
+function guiMeshToonMaterial( gui, mesh, material ) {
+
+	var data = {
+		color: material.color.getHex(),
+		map: diffuseMapKeys[ 0 ],
+		gradientMap: gradientMapKeys[ 1 ],
+		alphaMap: alphaMapKeys[ 0 ]
+	};
+
+	var folder = gui.addFolder( 'THREE.MeshToonMaterial' );
+
+	folder.addColor( data, 'color' ).onChange( handleColorChange( material.color ) );
+
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'gradientMap', gradientMapKeys ).onChange( updateTexture( material, 'gradientMap', gradientMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
 
 }
 
@@ -396,11 +495,10 @@ function guiMeshStandardMaterial( gui, mesh, material, geometry ) {
 	var data = {
 		color: material.color.getHex(),
 		emissive: material.emissive.getHex(),
-		envMaps: envMapKeys,
-		map: textureMapKeys,
-		lightMap: textureMapKeys,
-		specularMap: textureMapKeys,
-		alphaMap: textureMapKeys
+		envMaps: envMapKeys[ 0 ],
+		map: diffuseMapKeys[ 0 ],
+		roughnessMap: roughnessMapKeys[ 0 ],
+		alphaMap: alphaMapKeys[ 0 ]
 	};
 
 	var folder = gui.addFolder( 'THREE.MeshStandardMaterial' );
@@ -416,11 +514,11 @@ function guiMeshStandardMaterial( gui, mesh, material, geometry ) {
 	folder.add( material, 'vertexColors', constants.colors ).onChange( needsUpdate( material, geometry ) );
 	folder.add( material, 'fog' );
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
-	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( material, 'lightMap', textureMaps ) );
-	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'roughnessMap', roughnessMapKeys ).onChange( updateTexture( material, 'roughnessMap', roughnessMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
 
-	// TODO roughnessMap and metalnessMap
+	// TODO metalnessMap
 
 }
 
@@ -429,11 +527,10 @@ function guiMeshPhysicalMaterial( gui, mesh, material, geometry ) {
 	var data = {
 		color: material.color.getHex(),
 		emissive: material.emissive.getHex(),
-		envMaps: envMapKeys,
-		map: textureMapKeys,
-		lightMap: textureMapKeys,
-		specularMap: textureMapKeys,
-		alphaMap: textureMapKeys
+		envMaps: envMapKeys[ 0 ],
+		map: diffuseMapKeys[ 0 ],
+		roughnessMap: roughnessMapKeys[ 0 ],
+		alphaMap: alphaMapKeys[ 0 ]
 	};
 
 	var folder = gui.addFolder( 'THREE.MeshPhysicalMaterial' );
@@ -452,11 +549,11 @@ function guiMeshPhysicalMaterial( gui, mesh, material, geometry ) {
 	folder.add( material, 'vertexColors', constants.colors ).onChange( needsUpdate( material, geometry ) );
 	folder.add( material, 'fog' );
 	folder.add( data, 'envMaps', envMapKeys ).onChange( updateTexture( material, 'envMap', envMaps ) );
-	folder.add( data, 'map', textureMapKeys ).onChange( updateTexture( material, 'map', textureMaps ) );
-	folder.add( data, 'lightMap', textureMapKeys ).onChange( updateTexture( material, 'lightMap', textureMaps ) );
-	folder.add( data, 'alphaMap', textureMapKeys ).onChange( updateTexture( material, 'alphaMap', textureMaps ) );
+	folder.add( data, 'map', diffuseMapKeys ).onChange( updateTexture( material, 'map', diffuseMaps ) );
+	folder.add( data, 'roughnessMap', roughnessMapKeys ).onChange( updateTexture( material, 'roughnessMap', roughnessMaps ) );
+	folder.add( data, 'alphaMap', alphaMapKeys ).onChange( updateTexture( material, 'alphaMap', alphaMaps ) );
 
-	// TODO roughnessMap and metalnessMap
+	// TODO metalnessMap
 
 }
 
@@ -487,6 +584,16 @@ function chooseFromHash( gui, mesh, geometry ) {
 
 			break;
 
+		case 'MeshMatcapMaterial' :
+
+			material = new THREE.MeshMatcapMaterial( { matcap: matcaps.porcelainWhite } );
+			guiMaterial( gui, mesh, material, geometry );
+			guiMeshMatcapMaterial( gui, mesh, material, geometry );
+
+			return material;
+
+			break;
+
 		case 'MeshPhongMaterial' :
 
 			material = new THREE.MeshPhongMaterial( { color: 0x2194CE } );
@@ -497,6 +604,21 @@ function chooseFromHash( gui, mesh, geometry ) {
 
 			break;
 
+		case 'MeshToonMaterial' :
+
+			material = new THREE.MeshToonMaterial( { color: 0x2194CE, gradientMap: gradientMaps.threeTone } );
+			guiMaterial( gui, mesh, material, geometry );
+			guiMeshToonMaterial( gui, mesh, material, geometry );
+
+			// only use a single point light
+
+			lights[ 0 ].visible = false;
+			lights[ 2 ].visible = false;
+
+			return material;
+
+			break;
+
 		case 'MeshStandardMaterial' :
 
 			material = new THREE.MeshStandardMaterial( { color: 0x2194CE } );

+ 4 - 0
editor/index.html

@@ -6,6 +6,7 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link rel="apple-touch-icon" href="images/icon.png">
 		<link rel="manifest" href="manifest.json">
+		<link rel="shortcut icon" href="../favicon.ico" />
 	</head>
 	<body ontouchstart="">
 		<script type="text/javascript">
@@ -140,8 +141,11 @@
 		<script src="js/Sidebar.Geometry.CircleGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.CylinderGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.IcosahedronGeometry.js"></script>
+		<script src="js/Sidebar.Geometry.OctahedronGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.PlaneGeometry.js"></script>
+		<script src="js/Sidebar.Geometry.RingGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.SphereGeometry.js"></script>
+		<script src="js/Sidebar.Geometry.TetrahedronGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.TorusGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.TorusKnotGeometry.js"></script>
 		<script src="js/Sidebar.Geometry.TubeGeometry.js"></script>

+ 7 - 1
editor/js/Editor.js

@@ -482,7 +482,11 @@ Editor.prototype = {
 
 	focus: function ( object ) {
 
-		this.signals.objectFocused.dispatch( object );
+		if ( object !== undefined ) {
+
+			this.signals.objectFocused.dispatch( object );
+
+		}
 
 	},
 
@@ -498,6 +502,8 @@ Editor.prototype = {
 		this.storage.clear();
 
 		this.camera.copy( this.DEFAULT_CAMERA );
+		this.scene.name = "Scene";
+		this.scene.userData = {};
 		this.scene.background.setHex( 0xaaaaaa );
 		this.scene.fog = null;
 

+ 2 - 2
editor/js/History.js

@@ -173,7 +173,7 @@ History.prototype = {
 
 		// Append Undos to History
 
-		for ( var i = 0 ; i < this.undos.length; i ++ ) {
+		for ( var i = 0; i < this.undos.length; i ++ ) {
 
 			if ( this.undos[ i ].hasOwnProperty( "json" ) ) {
 
@@ -185,7 +185,7 @@ History.prototype = {
 
 		// Append Redos to History
 
-		for ( var i = 0 ; i < this.redos.length; i ++ ) {
+		for ( var i = 0; i < this.redos.length; i ++ ) {
 
 			if ( this.redos[ i ].hasOwnProperty( "json" ) ) {
 

+ 63 - 0
editor/js/Menubar.Add.js

@@ -86,6 +86,22 @@ Menubar.Add = function ( editor ) {
 	} );
 	options.add( option );
 
+	// Ring
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/ring' ) );
+	option.onClick( function () {
+
+		var geometry = new THREE.RingBufferGeometry( 0.5, 1, 8, 1, 0, Math.PI * 2 );
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
+		mesh.name = 'Ring';
+
+		editor.execute( new AddObjectCommand( mesh ) );
+
+	} );
+	options.add( option );
+
 	// Cylinder
 
 	var option = new UI.Row();
@@ -134,6 +150,38 @@ Menubar.Add = function ( editor ) {
 	} );
 	options.add( option );
 
+	// Octahedron
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/octahedron' ) );
+	option.onClick( function () {
+
+		var geometry = new THREE.OctahedronBufferGeometry( 1, 0 );
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
+		mesh.name = 'Octahedron';
+
+		editor.execute( new AddObjectCommand( mesh ) );
+
+	} );
+	options.add( option );
+
+	// Tetrahedron
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/tetrahedron' ) );
+	option.onClick( function () {
+
+		var geometry = new THREE.TetrahedronBufferGeometry( 1, 0 );
+		var mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
+		mesh.name = 'Tetrahedron';
+
+		editor.execute( new AddObjectCommand( mesh ) );
+
+	} );
+	options.add( option );
+
 	// Torus
 
 	var option = new UI.Row();
@@ -388,6 +436,21 @@ Menubar.Add = function ( editor ) {
 	} );
 	options.add( option );
 
+	// OrthographicCamera
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( strings.getKey( 'menubar/add/orthographiccamera' ) );
+	option.onClick( function () {
+
+		var camera = new THREE.OrthographicCamera();
+		camera.name = 'OrthographicCamera';
+
+		editor.execute( new AddObjectCommand( camera ) );
+
+	} );
+	options.add( option );
+
 	return container;
 
 };

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

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

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

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

+ 22 - 19
editor/js/Script.js

@@ -20,6 +20,7 @@ var Script = function ( editor ) {
 	header.add( title );
 
 	var buttonSVG = ( function () {
+
 		var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
 		svg.setAttribute( 'width', 32 );
 		svg.setAttribute( 'height', 32 );
@@ -28,6 +29,7 @@ var Script = function ( editor ) {
 		path.setAttribute( 'stroke', '#fff' );
 		svg.appendChild( path );
 		return svg;
+
 	} )();
 
 	var close = new UI.Element( buttonSVG );
@@ -80,7 +82,7 @@ var Script = function ( editor ) {
 
 			if ( ! validate( value ) ) return;
 
-			if ( typeof( currentScript ) === 'object' ) {
+			if ( typeof ( currentScript ) === 'object' ) {
 
 				if ( value !== currentScript.source ) {
 
@@ -88,6 +90,7 @@ var Script = function ( editor ) {
 
 				}
 				return;
+
 			}
 
 			if ( currentScript !== 'programInfo' ) return;
@@ -118,7 +121,7 @@ var Script = function ( editor ) {
 
 		}, 300 );
 
-	});
+	} );
 
 	// prevent backspace from deleting objects
 	var wrapper = codemirror.getWrapperElement();
@@ -177,7 +180,7 @@ var Script = function ( editor ) {
 					for ( var i = 0; i < errors.length; i ++ ) {
 
 						var error = errors[ i ];
-						error.message = error.message.replace(/Line [0-9]+: /, '');
+						error.message = error.message.replace( /Line [0-9]+: /, '' );
 
 					}
 
@@ -189,7 +192,7 @@ var Script = function ( editor ) {
 
 					jsonlint.parseError = function ( message, info ) {
 
-						message = message.split('\n')[3];
+						message = message.split( '\n' )[ 3 ];
 
 						errors.push( {
 
@@ -217,11 +220,11 @@ var Script = function ( editor ) {
 					try {
 
 						var shaderType = currentScript === 'vertexShader' ?
-								glslprep.Shader.VERTEX : glslprep.Shader.FRAGMENT;
+							glslprep.Shader.VERTEX : glslprep.Shader.FRAGMENT;
 
 						glslprep.parseGlsl( string, shaderType );
 
-					} catch( error ) {
+					} catch ( error ) {
 
 						if ( error instanceof glslprep.SyntaxError ) {
 
@@ -254,7 +257,7 @@ var Script = function ( editor ) {
 
 					for ( var i = 0, n = programs.length; i !== n; ++ i ) {
 
-						var diagnostics = programs[i].diagnostics;
+						var diagnostics = programs[ i ].diagnostics;
 
 						if ( diagnostics === undefined ||
 								diagnostics.material !== currentObject.material ) continue;
@@ -262,7 +265,7 @@ var Script = function ( editor ) {
 						if ( ! diagnostics.runnable ) valid = false;
 
 						var shaderInfo = diagnostics[ currentScript ];
-						var lineOffset = shaderInfo.prefix.split(/\r\n|\r|\n/).length;
+						var lineOffset = shaderInfo.prefix.split( /\r\n|\r|\n/ ).length;
 
 						while ( true ) {
 
@@ -305,7 +308,7 @@ var Script = function ( editor ) {
 
 			return valid !== undefined ? valid : errors.length === 0;
 
-		});
+		} );
 
 	};
 
@@ -317,23 +320,23 @@ var Script = function ( editor ) {
 	} );
 
 	codemirror.setOption( 'extraKeys', {
-		'Ctrl-Space': function(cm) { server.complete(cm); },
-		'Ctrl-I': function(cm) { server.showType(cm); },
-		'Ctrl-O': function(cm) { server.showDocs(cm); },
-		'Alt-.': function(cm) { server.jumpToDef(cm); },
-		'Alt-,': function(cm) { server.jumpBack(cm); },
-		'Ctrl-Q': function(cm) { server.rename(cm); },
-		'Ctrl-.': function(cm) { server.selectName(cm); }
+		'Ctrl-Space': function ( cm ) { server.complete( cm ); },
+		'Ctrl-I': function ( cm ) { server.showType( cm ); },
+		'Ctrl-O': function ( cm ) { server.showDocs( cm ); },
+		'Alt-.': function ( cm ) { server.jumpToDef( cm ); },
+		'Alt-,': function ( cm ) { server.jumpBack( cm ); },
+		'Ctrl-Q': function ( cm ) { server.rename( cm ); },
+		'Ctrl-.': function ( cm ) { server.selectName( cm ); }
 	} );
 
-	codemirror.on( 'cursorActivity', function( cm ) {
+	codemirror.on( 'cursorActivity', function ( cm ) {
 
 		if ( currentMode !== 'javascript' ) return;
 		server.updateArgHints( cm );
 
 	} );
 
-	codemirror.on( 'keypress', function( cm, kb ) {
+	codemirror.on( 'keypress', function ( cm, kb ) {
 
 		if ( currentMode !== 'javascript' ) return;
 		var typed = String.fromCharCode( kb.which || kb.keyCode );
@@ -358,7 +361,7 @@ var Script = function ( editor ) {
 
 		var mode, name, source;
 
-		if ( typeof( script ) === 'object' ) {
+		if ( typeof ( script ) === 'object' ) {
 
 			mode = 'javascript';
 			name = script.name;

+ 54 - 0
editor/js/Sidebar.Geometry.OctahedronGeometry.js

@@ -0,0 +1,54 @@
+/**
+ * @author Temdog007 / http://github.com/Temdog007
+ */
+
+Sidebar.Geometry.OctahedronGeometry = function ( editor, object ) {
+
+	var strings = editor.strings;
+
+	var signals = editor.signals;
+
+	var container = new UI.Row();
+
+	var geometry = object.geometry;
+	var parameters = geometry.parameters;
+
+	// radius
+
+	var radiusRow = new UI.Row();
+	var radius = new UI.Number( parameters.radius ).onChange( update );
+
+	radiusRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/octahedron_geometry/radius' ) ).setWidth( '90px' ) );
+	radiusRow.add( radius );
+
+	container.add( radiusRow );
+
+	// detail
+
+	var detailRow = new UI.Row();
+	var detail = new UI.Integer( parameters.detail ).setRange( 0, Infinity ).onChange( update );
+
+	detailRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/octahedron_geometry/detail' ) ).setWidth( '90px' ) );
+	detailRow.add( detail );
+
+	container.add( detailRow );
+
+
+	//
+
+	function update() {
+
+		editor.execute( new SetGeometryCommand( object, new THREE[ geometry.type ](
+			radius.getValue(),
+			detail.getValue()
+		) ) );
+
+		signals.objectChanged.dispatch( object );
+
+	}
+
+	return container;
+
+};
+
+Sidebar.Geometry.OctahedronBufferGeometry = Sidebar.Geometry.OctahedronGeometry;

+ 95 - 0
editor/js/Sidebar.Geometry.RingGeometry.js

@@ -0,0 +1,95 @@
+/**
+ * @author Temdog007 / http://github.com/Temdog007
+ */
+
+Sidebar.Geometry.RingGeometry = function ( editor, object ) {
+
+	var strings = editor.strings;
+
+	var signals = editor.signals;
+
+	var container = new UI.Row();
+
+	var geometry = object.geometry;
+	var parameters = geometry.parameters;
+
+	// innerRadius
+
+	var innerRadiusRow = new UI.Row();
+	var innerRadius = new UI.Number( parameters.innerRadius ).onChange( update );
+
+	innerRadiusRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/innerRadius' ) ).setWidth( '90px' ) );
+	innerRadiusRow.add( innerRadius );
+
+	container.add( innerRadiusRow );
+
+	// outerRadius
+
+	var outerRadiusRow = new UI.Row();
+	var outerRadius = new UI.Number( parameters.outerRadius ).onChange( update );
+
+	outerRadiusRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/outerRadius' ) ).setWidth( '90px' ) );
+	outerRadiusRow.add( outerRadius );
+
+	container.add( outerRadiusRow );
+
+	// thetaSegments
+
+	var thetaSegmentsRow = new UI.Row();
+	var thetaSegments = new UI.Integer( parameters.thetaSegments ).setRange( 3, Infinity ).onChange( update );
+
+	thetaSegmentsRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/thetaSegments' ) ).setWidth( '90px' ) );
+	thetaSegmentsRow.add( thetaSegments );
+
+	container.add( thetaSegmentsRow );
+
+	// phiSegments
+
+	var phiSegmentsRow = new UI.Row();
+	var phiSegments = new UI.Integer( parameters.phiSegments ).setRange( 3, Infinity ).onChange( update );
+
+	phiSegmentsRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/phiSegments' ) ).setWidth( '90px' ) );
+	phiSegmentsRow.add( phiSegments );
+
+	container.add( phiSegmentsRow );
+
+	// thetaStart
+
+	var thetaStartRow = new UI.Row();
+	var thetaStart = new UI.Number( parameters.thetaStart * THREE.Math.RAD2DEG ).setStep( 10 ).onChange( update );
+
+	thetaStartRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/thetastart' ) ).setWidth( '90px' ) );
+	thetaStartRow.add( thetaStart );
+
+	container.add( thetaStartRow );
+
+	// thetaLength
+
+	var thetaLengthRow = new UI.Row();
+	var thetaLength = new UI.Number( parameters.thetaLength * THREE.Math.RAD2DEG ).setStep( 10 ).onChange( update );
+
+	thetaLengthRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/ring_geometry/thetalength' ) ).setWidth( '90px' ) );
+	thetaLengthRow.add( thetaLength );
+
+	container.add( thetaLengthRow );
+
+	//
+
+	function update() {
+
+		editor.execute( new SetGeometryCommand( object, new THREE[ geometry.type ](
+			innerRadius.getValue(),
+			outerRadius.getValue(),
+			thetaSegments.getValue(),
+			phiSegments.getValue(),
+			thetaStart.getValue() * THREE.Math.DEG2RAD,
+			thetaLength.getValue() * THREE.Math.DEG2RAD
+		) ) );
+
+	}
+
+	return container;
+
+};
+
+Sidebar.Geometry.RingBufferGeometry = Sidebar.Geometry.RingGeometry;

+ 55 - 0
editor/js/Sidebar.Geometry.TetrahedronGeometry.js

@@ -0,0 +1,55 @@
+/**
+ * @author Temdog007 / http://github.com/Temdog007
+ */
+
+
+Sidebar.Geometry.TetrahedronGeometry = function ( editor, object ) {
+
+	var strings = editor.strings;
+
+	var signals = editor.signals;
+
+	var container = new UI.Row();
+
+	var geometry = object.geometry;
+	var parameters = geometry.parameters;
+
+	// radius
+
+	var radiusRow = new UI.Row();
+	var radius = new UI.Number( parameters.radius ).onChange( update );
+
+	radiusRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/tetrahedron_geometry/radius' ) ).setWidth( '90px' ) );
+	radiusRow.add( radius );
+
+	container.add( radiusRow );
+
+	// detail
+
+	var detailRow = new UI.Row();
+	var detail = new UI.Integer( parameters.detail ).setRange( 0, Infinity ).onChange( update );
+
+	detailRow.add( new UI.Text( strings.getKey( 'sidebar/geometry/tetrahedron_geometry/detail' ) ).setWidth( '90px' ) );
+	detailRow.add( detail );
+
+	container.add( detailRow );
+
+
+	//
+
+	function update() {
+
+		editor.execute( new SetGeometryCommand( object, new THREE[ geometry.type ](
+			radius.getValue(),
+			detail.getValue()
+		) ) );
+
+		signals.objectChanged.dispatch( object );
+
+	}
+
+	return container;
+
+};
+
+Sidebar.Geometry.TetrahedronBufferGeometry = Sidebar.Geometry.TetrahedronGeometry;

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

@@ -179,6 +179,8 @@ Sidebar.Geometry = function ( editor ) {
 
 			}
 
+			if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
 			geometryBoundingSphere.setValue( Math.floor( geometry.boundingSphere.radius * 1000 ) / 1000 );
 
 		} else {

+ 161 - 6
editor/js/Sidebar.Material.js

@@ -83,10 +83,14 @@ Sidebar.Material = function ( editor ) {
 		'MeshDepthMaterial': 'MeshDepthMaterial',
 		'MeshNormalMaterial': 'MeshNormalMaterial',
 		'MeshLambertMaterial': 'MeshLambertMaterial',
+		'MeshMatcapMaterial': 'MeshMatcapMaterial',
 		'MeshPhongMaterial': 'MeshPhongMaterial',
+		'MeshToonMaterial': 'MeshToonMaterial',
 		'MeshStandardMaterial': 'MeshStandardMaterial',
 		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
 		'ShaderMaterial': 'ShaderMaterial',
+		'ShadowMaterial': 'ShadowMaterial',
 		'SpriteMaterial': 'SpriteMaterial'
 
 	} ).setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
@@ -257,6 +261,20 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialVertexColorsRow );
 
+	// depth packing
+
+	var materialDepthPackingRow = new UI.Row();
+	var materialDepthPacking = new UI.Select().setOptions( {
+		[ THREE.BasicDepthPacking ]: 'BasicDepthPacking',
+		[ THREE.RGBADepthPacking ]: 'RGBADepthPacking'
+	} );
+	materialDepthPacking.onChange( update );
+
+	materialDepthPackingRow.add( new UI.Text( strings.getKey( 'sidebar/material/depthPacking' ) ).setWidth( '90px' ) );
+	materialDepthPackingRow.add( materialDepthPacking );
+
+	container.add( materialDepthPackingRow );
+
 	// skinning
 
 	var materialSkinningRow = new UI.Row();
@@ -271,7 +289,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialMapRow = new UI.Row();
 	var materialMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialMap = new UI.Texture().onChange( update );
+	var materialMap = new UI.Texture().onChange( updateMaterial );
 
 	materialMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/map' ) ).setWidth( '90px' ) );
 	materialMapRow.add( materialMapEnabled );
@@ -279,6 +297,18 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialMapRow );
 
+	// matcap map
+
+	var materialMatcapMapRow = new UI.Row();
+	var materialMatcapMapEnabled = new UI.Checkbox( false ).onChange( update );
+	var materialMatcapMap = new UI.Texture().onChange( update );
+
+	materialMatcapMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/matcap' ) ).setWidth( '90px' ) );
+	materialMatcapMapRow.add( materialMatcapMapEnabled );
+	materialMatcapMapRow.add( materialMatcapMap );
+
+	container.add( materialMatcapMapRow );
+
 	// alpha map
 
 	var materialAlphaMapRow = new UI.Row();
@@ -371,7 +401,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialEnvMapRow = new UI.Row();
 	var materialEnvMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialEnvMap = new UI.Texture( THREE.SphericalReflectionMapping ).onChange( update );
+	var materialEnvMap = new UI.Texture( THREE.SphericalReflectionMapping ).onChange( updateMaterial );
 	var materialReflectivity = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
 
 	materialEnvMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/envmap' ) ).setWidth( '90px' ) );
@@ -411,7 +441,7 @@ Sidebar.Material = function ( editor ) {
 
 	var materialEmissiveMapRow = new UI.Row();
 	var materialEmissiveMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialEmissiveMap = new UI.Texture().onChange( update );
+	var materialEmissiveMap = new UI.Texture().onChange( updateMaterial );
 
 	materialEmissiveMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/emissivemap' ) ).setWidth( '90px' ) );
 	materialEmissiveMapRow.add( materialEmissiveMapEnabled );
@@ -419,6 +449,18 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialEmissiveMapRow );
 
+	// gradient map
+
+	var materialGradientMapRow = new UI.Row();
+	var materialGradientMapEnabled = new UI.Checkbox( false ).onChange( update );
+	var materialGradientMap = new UI.Texture().onChange( update );
+
+	materialGradientMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/gradientmap' ) ).setWidth( '90px' ) );
+	materialGradientMapRow.add( materialGradientMapEnabled );
+	materialGradientMapRow.add( materialGradientMap );
+
+	container.add( materialGradientMapRow );
+
 	// side
 
 	var materialSideRow = new UI.Row();
@@ -438,7 +480,7 @@ Sidebar.Material = function ( editor ) {
 	// shading
 
 	var materialShadingRow = new UI.Row();
-	var materialShading = new UI.Checkbox(false).setLeft( '100px' ).onChange( update );
+	var materialShading = new UI.Checkbox( false ).setLeft( '100px' ).onChange( update );
 
 	materialShadingRow.add( new UI.Text( strings.getKey( 'sidebar/material/flatshaded' ) ).setWidth( '90px' ) );
 	materialShadingRow.add( materialShading );
@@ -537,10 +579,16 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material instanceof THREE[ materialClass.getValue() ] === false ) {
+			if ( material.type !== materialClass.getValue() ) {
 
 				material = new THREE[ materialClass.getValue() ]();
 
+				if ( material.type == "RawShaderMaterial" ) {
+
+					material.vertexShader = vertexShaderVariables + material.vertexShader;
+
+				}
+
 				editor.execute( new SetMaterialCommand( currentObject, material, currentMaterialSlot ), 'New Material: ' + materialClass.getValue() );
 				// TODO Copy other references in the scene graph
 				// keeping name and UUID then.
@@ -610,6 +658,17 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.depthPacking !== undefined ) {
+
+				var depthPacking = parseInt( materialDepthPacking.getValue() );
+				if ( material.depthPacking !== depthPacking ) {
+
+					editor.execute( new SetMaterialValueCommand( currentObject, 'depthPacking', depthPacking, currentMaterialSlot ) );
+
+				}
+
+			}
+
 			if ( material.skinning !== undefined && material.skinning !== materialSkinning.getValue() ) {
 
 				editor.execute( new SetMaterialValueCommand( currentObject, 'skinning', materialSkinning.getValue(), currentMaterialSlot ) );
@@ -637,6 +696,27 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.matcap !== undefined ) {
+
+				var mapEnabled = materialMatcapMapEnabled.getValue() === true;
+
+				if ( objectHasUvs ) {
+
+					var matcap = mapEnabled ? materialMatcapMap.getValue() : null;
+					if ( material.matcap !== matcap ) {
+
+						editor.execute( new SetMaterialMapCommand( currentObject, 'matcap', matcap, currentMaterialSlot ) );
+
+					}
+
+				} else {
+
+					if ( mapEnabled ) textureWarning = true;
+
+				}
+
+			}
+
 			if ( material.alphaMap !== undefined ) {
 
 				var mapEnabled = materialAlphaMapEnabled.getValue() === true;
@@ -891,6 +971,20 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
+			if ( material.gradientMap !== undefined ) {
+
+				var gradientMapEnabled = materialGradientMapEnabled.getValue() === true;
+
+				var gradientMap = gradientMapEnabled ? materialGradientMap.getValue() : null;
+
+				if ( material.gradientMap !== gradientMap ) {
+
+					editor.execute( new SetMaterialMapCommand( currentObject, 'gradientMap', gradientMap, currentMaterialSlot ) );
+
+				}
+
+			}
+
 			if ( material.side !== undefined ) {
 
 				var side = parseInt( materialSide.getValue() );
@@ -945,7 +1039,7 @@ Sidebar.Material = function ( editor ) {
 
 			if ( material.wireframe !== undefined && material.wireframe !== materialWireframe.getValue() ) {
 
-				editor.execute( new SetMaterialValueCommand( currentObject, 'wireframe', materialWireframe.getValue(), currentMaterialSlot) );
+				editor.execute( new SetMaterialValueCommand( currentObject, 'wireframe', materialWireframe.getValue(), currentMaterialSlot ) );
 
 			}
 
@@ -967,6 +1061,28 @@ Sidebar.Material = function ( editor ) {
 
 	}
 
+	function updateMaterial( texture ) {
+
+		if ( texture !== null ) {
+
+			if ( texture.encoding !== THREE.sRGBEncoding ) {
+
+				texture.encoding = THREE.sRGBEncoding;
+				var object = currentObject;
+				if ( object !== null ) {
+
+					object.material.needsUpdate = true;
+
+				}
+
+			}
+
+		}
+
+		update();
+
+	}
+
 	//
 
 	function setRowVisibility() {
@@ -983,8 +1099,10 @@ Sidebar.Material = function ( editor ) {
 			'clearCoatRoughness': materialClearCoatRoughnessRow,
 			'vertexShader': materialProgramRow,
 			'vertexColors': materialVertexColorsRow,
+			'depthPacking': materialDepthPackingRow,
 			'skinning': materialSkinningRow,
 			'map': materialMapRow,
+			'matcap': materialMatcapMapRow,
 			'alphaMap': materialAlphaMapRow,
 			'bumpMap': materialBumpMapRow,
 			'normalMap': materialNormalMapRow,
@@ -996,6 +1114,7 @@ Sidebar.Material = function ( editor ) {
 			'lightMap': materialLightMapRow,
 			'aoMap': materialAOMapRow,
 			'emissiveMap': materialEmissiveMapRow,
+			'gradientMap': materialGradientMapRow,
 			'side': materialSideRow,
 			'flatShading': materialShadingRow,
 			'blending': materialBlendingRow,
@@ -1122,6 +1241,12 @@ Sidebar.Material = function ( editor ) {
 
 		}
 
+		if ( material.depthPacking !== undefined ) {
+
+			materialDepthPacking.setValue( material.depthPacking );
+
+		}
+
 		if ( material.skinning !== undefined ) {
 
 			materialSkinning.setValue( material.skinning );
@@ -1140,6 +1265,18 @@ Sidebar.Material = function ( editor ) {
 
 		}
 
+		if ( material.matcap !== undefined ) {
+
+			materialMatcapMapEnabled.setValue( material.matcap !== null );
+
+			if ( material.matcap !== null || resetTextureSelectors ) {
+
+				materialMatcapMap.setValue( material.matcap );
+
+			}
+
+		}
+
 		if ( material.alphaMap !== undefined ) {
 
 			materialAlphaMapEnabled.setValue( material.alphaMap !== null );
@@ -1240,6 +1377,18 @@ Sidebar.Material = function ( editor ) {
 
 		}
 
+		if ( material.gradientMap !== undefined ) {
+
+			materialGradientMapEnabled.setValue( material.gradientMap !== null );
+
+			if ( material.gradientMap !== null || resetTextureSelectors ) {
+
+				materialGradientMap.setValue( material.gradientMap );
+
+			}
+
+		}
+
 		if ( material.reflectivity !== undefined ) {
 
 			materialReflectivity.setValue( material.reflectivity );
@@ -1377,6 +1526,12 @@ Sidebar.Material = function ( editor ) {
 
 	} );
 
+	var vertexShaderVariables = [
+		'uniform mat4 projectionMatrix;',
+		'uniform mat4 modelViewMatrix;\n',
+		'attribute vec3 position;\n\n',
+	].join( '\n' );
+
 	return container;
 
 };

+ 112 - 6
editor/js/Sidebar.Object.js

@@ -146,6 +146,46 @@ Sidebar.Object = function ( editor ) {
 
 	container.add( objectFovRow );
 
+	// left
+
+	var objectLeftRow = new UI.Row();
+	var objectLeft = new UI.Number().onChange( update );
+
+	objectLeftRow.add( new UI.Text( strings.getKey( 'sidebar/object/left' ) ).setWidth( '90px' ) );
+	objectLeftRow.add( objectLeft );
+
+	container.add( objectLeftRow );
+
+	// right
+
+	var objectRightRow = new UI.Row();
+	var objectRight = new UI.Number().onChange( update );
+
+	objectRightRow.add( new UI.Text( strings.getKey( 'sidebar/object/right' ) ).setWidth( '90px' ) );
+	objectRightRow.add( objectRight );
+
+	container.add( objectRightRow );
+
+	// top
+
+	var objectTopRow = new UI.Row();
+	var objectTop = new UI.Number().onChange( update );
+
+	objectTopRow.add( new UI.Text( strings.getKey( 'sidebar/object/top' ) ).setWidth( '90px' ) );
+	objectTopRow.add( objectTop );
+
+	container.add( objectTopRow );
+
+	// bottom
+
+	var objectBottomRow = new UI.Row();
+	var objectBottom = new UI.Number().onChange( update );
+
+	objectBottomRow.add( new UI.Text( strings.getKey( 'sidebar/object/bottom' ) ).setWidth( '90px' ) );
+	objectBottomRow.add( objectBottom );
+
+	container.add( objectBottomRow );
+
 	// near
 
 	var objectNearRow = new UI.Row();
@@ -400,15 +440,53 @@ Sidebar.Object = function ( editor ) {
 
 			}
 
+			if ( object.left !== undefined && Math.abs( object.left - objectLeft.getValue() ) >= 0.01 ) {
+
+				editor.execute( new SetValueCommand( object, 'left', objectLeft.getValue() ) );
+				object.updateProjectionMatrix();
+
+			}
+
+			if ( object.right !== undefined && Math.abs( object.right - objectRight.getValue() ) >= 0.01 ) {
+
+				editor.execute( new SetValueCommand( object, 'right', objectRight.getValue() ) );
+				object.updateProjectionMatrix();
+
+			}
+
+			if ( object.top !== undefined && Math.abs( object.top - objectTop.getValue() ) >= 0.01 ) {
+
+				editor.execute( new SetValueCommand( object, 'top', objectTop.getValue() ) );
+				object.updateProjectionMatrix();
+
+			}
+
+			if ( object.bottom !== undefined && Math.abs( object.bottom - objectBottom.getValue() ) >= 0.01 ) {
+
+				editor.execute( new SetValueCommand( object, 'bottom', objectBottom.getValue() ) );
+				object.updateProjectionMatrix();
+
+			}
+
 			if ( object.near !== undefined && Math.abs( object.near - objectNear.getValue() ) >= 0.01 ) {
 
 				editor.execute( new SetValueCommand( object, 'near', objectNear.getValue() ) );
+				if ( object.isOrthographicCamera ) {
+
+					object.updateProjectionMatrix();
+
+				}
 
 			}
 
 			if ( object.far !== undefined && Math.abs( object.far - objectFar.getValue() ) >= 0.01 ) {
 
 				editor.execute( new SetValueCommand( object, 'far', objectFar.getValue() ) );
+				if ( object.isOrthographicCamera ) {
+
+					object.updateProjectionMatrix();
+
+				}
 
 			}
 
@@ -518,17 +596,21 @@ Sidebar.Object = function ( editor ) {
 
 		var properties = {
 			'fov': objectFovRow,
+			'left': objectLeftRow,
+			'right': objectRightRow,
+			'top': objectTopRow,
+			'bottom': objectBottomRow,
 			'near': objectNearRow,
 			'far': objectFarRow,
 			'intensity': objectIntensityRow,
 			'color': objectColorRow,
 			'groundColor': objectGroundColorRow,
-			'distance' : objectDistanceRow,
-			'angle' : objectAngleRow,
-			'penumbra' : objectPenumbraRow,
-			'decay' : objectDecayRow,
-			'castShadow' : objectShadowRow,
-			'receiveShadow' : objectReceiveShadow,
+			'distance': objectDistanceRow,
+			'angle': objectAngleRow,
+			'penumbra': objectPenumbraRow,
+			'decay': objectDecayRow,
+			'castShadow': objectShadowRow,
+			'receiveShadow': objectReceiveShadow,
 			'shadow': objectShadowRadius
 		};
 
@@ -617,6 +699,30 @@ Sidebar.Object = function ( editor ) {
 
 		}
 
+		if ( object.left !== undefined ) {
+
+			objectLeft.setValue( object.left );
+
+		}
+
+		if ( object.right !== undefined ) {
+
+			objectRight.setValue( object.right );
+
+		}
+
+		if ( object.top !== undefined ) {
+
+			objectTop.setValue( object.top );
+
+		}
+
+		if ( object.bottom !== undefined ) {
+
+			objectBottom.setValue( object.bottom );
+
+		}
+
 		if ( object.near !== undefined ) {
 
 			objectNear.setValue( object.near );

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

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

+ 47 - 0
editor/js/Strings.js

@@ -31,6 +31,7 @@ var Strings = function ( config ) {
 			'menubar/edit/clone': 'Clone',
 			'menubar/edit/delete': 'Delete (Del)',
 			'menubar/edit/minify_shaders': 'Minify Shaders',
+			'menubar/edit/fixcolormaps': 'Fix Color Maps',
 
 			'menubar/add': 'Add',
 			'menubar/add/group': 'Group',
@@ -38,8 +39,11 @@ var Strings = function ( config ) {
 			'menubar/add/box': 'Box',
 			'menubar/add/circle': 'Circle',
 			'menubar/add/cylinder': 'Cylinder',
+			'menubar/add/ring': 'Ring',
 			'menubar/add/sphere': 'Sphere',
 			'menubar/add/icosahedron': 'Icosahedron',
+			'menubar/add/octahedron': 'Octahedron',
+			'menubar/add/tetrahedron': 'Tetrahedron',
 			'menubar/add/torus': 'Torus',
 			'menubar/add/tube': 'Tube',
 			'menubar/add/torusknot': 'TorusKnot',
@@ -51,6 +55,7 @@ var Strings = function ( config ) {
 			'menubar/add/hemispherelight': 'HemisphereLight',
 			'menubar/add/ambientlight': 'AmbientLight',
 			'menubar/add/perspectivecamera': 'PerspectiveCamera',
+			'menubar/add/orthographiccamera': 'OrthographicCamera',
 
 			'menubar/status/autosave': 'autosave',
 
@@ -81,6 +86,10 @@ var Strings = function ( config ) {
 			'sidebar/object/rotation': 'Rotation',
 			'sidebar/object/scale': 'Scale',
 			'sidebar/object/fov': 'Fov',
+			'sidebar/object/left': 'Left',
+			'sidebar/object/right': 'Right',
+			'sidebar/object/top': 'Top',
+			'sidebar/object/bottom': 'Bottom',
 			'sidebar/object/near': 'Near',
 			'sidebar/object/far': 'Far',
 			'sidebar/object/intensity': 'Intensity',
@@ -132,6 +141,12 @@ var Strings = function ( config ) {
 			'sidebar/geometry/icosahedron_geometry/radius': 'Radius',
 			'sidebar/geometry/icosahedron_geometry/detail': 'Detail',
 
+			'sidebar/geometry/octahedron_geometry/radius': 'Radius',
+			'sidebar/geometry/octahedron_geometry/detail': 'Detail',
+
+			'sidebar/geometry/tetrahedron_geometry/radius': 'Radius',
+			'sidebar/geometry/tetrahedron_geometry/detail': 'Detail',
+
 			'sidebar/geometry/lathe_geometry/segments': 'Segments',
 			'sidebar/geometry/lathe_geometry/phistart': 'Phi start (°)',
 			'sidebar/geometry/lathe_geometry/philength': 'Phi length (°)',
@@ -142,6 +157,13 @@ var Strings = function ( config ) {
 			'sidebar/geometry/plane_geometry/widthsegments': 'Width segments',
 			'sidebar/geometry/plane_geometry/heightsegments': 'Height segments',
 
+			'sidebar/geometry/ring_geometry/innerRadius': 'Inner radius',
+			'sidebar/geometry/ring_geometry/outerRadius': 'Outer radius',
+			'sidebar/geometry/ring_geometry/thetaSegments': 'Theta segments',
+			'sidebar/geometry/ring_geometry/phiSegments': 'Phi segments',
+			'sidebar/geometry/ring_geometry/thetastart': 'Theta start',
+			'sidebar/geometry/ring_geometry/thetalength': 'Theta length',
+
 			'sidebar/geometry/sphere_geometry/radius': 'Radius',
 			'sidebar/geometry/sphere_geometry/widthsegments': 'Width segments',
 			'sidebar/geometry/sphere_geometry/heightsegments': 'Height segments',
@@ -183,6 +205,7 @@ var Strings = function ( config ) {
 			'sidebar/material/vertex': 'Vertex',
 			'sidebar/material/fragment': 'fragment',
 			'sidebar/material/color': 'Color',
+			'sidebar/material/depthPacking': 'Depth Packing',
 			'sidebar/material/roughness': 'Roughness',
 			'sidebar/material/metalness': 'Metalness',
 			'sidebar/material/emissive': 'Emissive',
@@ -195,6 +218,7 @@ var Strings = function ( config ) {
 			'sidebar/material/vertexcolors/face': 'Face',
 			'sidebar/material/vertexcolors/vertex': 'Vertex',
 			'sidebar/material/skinning': 'Skinning',
+			'sidebar/material/matcap': 'Matcap',
 			'sidebar/material/map': 'Map',
 			'sidebar/material/alphamap': 'Alpha Map',
 			'sidebar/material/bumpmap': 'Bump Map',
@@ -207,6 +231,7 @@ var Strings = function ( config ) {
 			'sidebar/material/lightmap': 'Light Map',
 			'sidebar/material/aomap': 'AO Map',
 			'sidebar/material/emissivemap': 'Emissive Map',
+			'sidebar/material/gradientmap': 'Gradient Map',
 			'sidebar/material/side': 'Side',
 			'sidebar/material/side/front': 'Front',
 			'sidebar/material/side/back': 'Back',
@@ -288,6 +313,7 @@ var Strings = function ( config ) {
 			'menubar/edit/clone': '拷贝',
 			'menubar/edit/delete': '删除 (Del)',
 			'menubar/edit/minify_shaders': '压缩着色器',
+			'menubar/edit/fixcolormaps': '修复颜色贴图',
 
 			'menubar/add': '添加',
 			'menubar/add/group': '组',
@@ -295,8 +321,11 @@ var Strings = function ( config ) {
 			'menubar/add/box': '正方体',
 			'menubar/add/circle': '圆',
 			'menubar/add/cylinder': '圆柱体',
+			'menubar/add/ring': '环',
 			'menubar/add/sphere': '球体',
 			'menubar/add/icosahedron': '二十面体',
+			'menubar/add/octahedron': '八面体',
+			'menubar/add/tetrahedron': '四面体',
 			'menubar/add/torus': '圆环体',
 			'menubar/add/torusknot': '环面纽结体',
 			'menubar/add/tube': '管',
@@ -308,6 +337,7 @@ var Strings = function ( config ) {
 			'menubar/add/hemispherelight': '半球光',
 			'menubar/add/ambientlight': '环境光',
 			'menubar/add/perspectivecamera': '透视相机',
+			'menubar/add/orthographiccamera': '正交相机',
 
 			'menubar/status/autosave': '自动保存',
 
@@ -338,6 +368,10 @@ var Strings = function ( config ) {
 			'sidebar/object/rotation': '旋转',
 			'sidebar/object/scale': '缩放',
 			'sidebar/object/fov': '视角',
+			'sidebar/object/left': '左',
+			'sidebar/object/right': '右',
+			'sidebar/object/top': '上',
+			'sidebar/object/bottom': '下',
 			'sidebar/object/near': '近点',
 			'sidebar/object/far': '远点',
 			'sidebar/object/intensity': '强度',
@@ -389,6 +423,12 @@ var Strings = function ( config ) {
 			'sidebar/geometry/icosahedron_geometry/radius': '半径',
 			'sidebar/geometry/icosahedron_geometry/detail': '面片分段',
 
+			'sidebar/geometry/octahedron_geometry/radius': '半径',
+			'sidebar/geometry/octahedron_geometry/detail': '面片分段',
+
+			'sidebar/geometry/tetrahedron_geometry/radius': '半径',
+			'sidebar/geometry/tetrahedron_geometry/detail': '面片分段',
+
 			'sidebar/geometry/lathe_geometry/segments': '分段',
 			'sidebar/geometry/lathe_geometry/phistart': '经度起点',
 			'sidebar/geometry/lathe_geometry/philength': '经度长度',
@@ -399,6 +439,13 @@ var Strings = function ( config ) {
 			'sidebar/geometry/plane_geometry/widthsegments': '宽度分段',
 			'sidebar/geometry/plane_geometry/heightsegments': '长度分段',
 
+			'sidebar/geometry/ring_geometry/innerRadius': '内半径',
+			'sidebar/geometry/ring_geometry/outerRadius': '外半径',
+			'sidebar/geometry/ring_geometry/thetaSegments': '弧度分段',
+			'sidebar/geometry/ring_geometry/phiSegments': '经度分段',
+			'sidebar/geometry/ring_geometry/thetastart': '弧度起点',
+			'sidebar/geometry/ring_geometry/thetalength': '弧度长度',
+
 			'sidebar/geometry/sphere_geometry/radius': '半径',
 			'sidebar/geometry/sphere_geometry/widthsegments': '宽度分段',
 			'sidebar/geometry/sphere_geometry/heightsegments': '长度分段',

+ 12 - 3
editor/js/Viewport.js

@@ -413,6 +413,7 @@ var Viewport = function ( editor ) {
 
 	signals.objectRemoved.add( function ( object ) {
 
+		controls.enabled = true; // see #14180
 		if ( object === transformControls.object ) {
 
 			transformControls.detach();
@@ -502,10 +503,18 @@ var Viewport = function ( editor ) {
 
 	signals.viewportCameraChanged.add( function ( viewportCamera ) {
 
-		camera = viewportCamera;
+		if ( viewportCamera.isPerspectiveCamera ) {
+
+			viewportCamera.aspect = editor.camera.aspect;
+			viewportCamera.projectionMatrix.copy( editor.camera.projectionMatrix );
+
+		} else if ( ! viewportCamera.isOrthographicCamera ) {
+
+			throw "Invalid camera set as viewport";
 
-		camera.aspect = editor.camera.aspect;
-		camera.projectionMatrix.copy( editor.camera.projectionMatrix );
+		}
+
+		camera = viewportCamera;
 
 		render();
 

+ 1 - 1
editor/js/libs/codemirror/mode/glsl.js

@@ -205,7 +205,7 @@
     "do for while if else in out inout float int void bool true false " +
     "lowp mediump highp precision invariant discard return mat2 mat3 " +
     "mat4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 sampler2D " +
-    "samplerCube struct gl_FragCoord gl_FragColor";
+    "samplerCube struct gl_FragCoord gl_FragColor gl_Position";
   var glslBuiltins = "radians degrees sin cos tan asin acos atan pow " +
     "exp log exp2 log2 sqrt inversesqrt abs sign floor ceil fract mod " +
     "min max clamp mix step smoothstep length distance dot cross " +

+ 38 - 38
editor/js/libs/three.html.js

@@ -2,74 +2,74 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
- THREE.HTMLGroup = function ( dom ) {
+THREE.HTMLGroup = function ( dom ) {
 
- 	THREE.Group.call( this );
+	THREE.Group.call( this );
 
- 	this.type = 'HTMLGroup';
+	this.type = 'HTMLGroup';
 
- 	/*
- 	dom.addEventListener( 'mousemove', function ( event ) {
+	/*
+	dom.addEventListener( 'mousemove', function ( event ) {
 
- 		console.log( 'mousemove' );
+		console.log( 'mousemove' );
 
- 	} );
+	} );
 
- 	dom.addEventListener( 'click', function ( event ) {
+	dom.addEventListener( 'click', function ( event ) {
 
- 		console.log( 'click' );
+		console.log( 'click' );
 
- 	} );
- 	*/
+	} );
+	*/
 
- };
+};
 
- THREE.HTMLGroup.prototype = Object.assign( Object.create( THREE.Group.prototype ), {
+THREE.HTMLGroup.prototype = Object.assign( Object.create( THREE.Group.prototype ), {
 
- 	constructor: THREE.HTMLGroup
+	constructor: THREE.HTMLGroup
 
- } );
+} );
 
- THREE.HTMLMesh = function ( dom ) {
+THREE.HTMLMesh = function ( dom ) {
 
- 	var texture = new THREE.HTMLTexture( dom );
+	var texture = new THREE.HTMLTexture( dom );
 
- 	var geometry = new THREE.PlaneBufferGeometry( texture.image.width * 0.05, texture.image.height * 0.05 );
- 	var material = new THREE.MeshBasicMaterial( { map: texture } );
+	var geometry = new THREE.PlaneBufferGeometry( texture.image.width * 0.05, texture.image.height * 0.05 );
+	var material = new THREE.MeshBasicMaterial( { map: texture } );
 
- 	THREE.Mesh.call( this, geometry, material );
+	THREE.Mesh.call( this, geometry, material );
 
- 	this.type = 'HTMLMesh';
+	this.type = 'HTMLMesh';
 
- };
+};
 
- THREE.HTMLMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
+THREE.HTMLMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), {
 
- 	constructor: THREE.HTMLMesh
+	constructor: THREE.HTMLMesh
 
- } );
+} );
 
- THREE.HTMLTexture = function ( dom ) {
+THREE.HTMLTexture = function ( dom ) {
 
- 	THREE.CanvasTexture.call( this, html2canvas( dom ) );
+	THREE.CanvasTexture.call( this, html2canvas( dom ) );
 
- 	this.dom = dom;
+	this.dom = dom;
 
- 	this.anisotropy = 16;
+	this.anisotropy = 16;
 
- };
+};
 
- THREE.HTMLTexture.prototype = Object.assign( Object.create( THREE.CanvasTexture.prototype ), {
+THREE.HTMLTexture.prototype = Object.assign( Object.create( THREE.CanvasTexture.prototype ), {
 
- 	constructor: THREE.HTMLTexture,
+	constructor: THREE.HTMLTexture,
 
- 	update: function () {
+	update: function () {
 
- 		console.log( 'yo!', this, this.dom );
+		console.log( 'yo!', this, this.dom );
 
- 		this.image = html2canvas( this.dom );
- 		this.needsUpdate = true;
+		this.image = html2canvas( this.dom );
+		this.needsUpdate = true;
 
- 	}
+	}
 
- } );
+} );

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

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

+ 5 - 3
examples/files.js

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

+ 1 - 0
examples/index.html

@@ -4,6 +4,7 @@
 		<title>three.js / examples</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link rel="shortcut icon" href="../favicon.ico" />
 		<style>
 
 			@font-face {

+ 102 - 0
examples/js/QuickHull.js

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

+ 25 - 22
examples/js/animation/MMDPhysics.js

@@ -994,14 +994,8 @@ THREE.MMDPhysics = ( function () {
 		_getWorldTransformForBone: function () {
 
 			var manager = this.manager;
-
-			var tr = manager.allocTransform();
-			this.body.getMotionState().getWorldTransform( tr );
-			var tr2 = manager.multiplyTransforms( tr, this.boneOffsetFormInverse );
-
-			manager.freeTransform( tr );
-
-			return tr2;
+			var tr = this.body.getCenterOfMassTransform();
+			return manager.multiplyTransforms( tr, this.boneOffsetFormInverse );
 
 		},
 
@@ -1057,7 +1051,11 @@ THREE.MMDPhysics = ( function () {
 			//this.bone.quaternion.multiply( thQ2 );
 
 			thQ3.setFromRotationMatrix( this.bone.matrix );
-			this.bone.quaternion.copy( thQ2.multiply( thQ3 ) );
+
+			// Renormalizing quaternion here because repeatedly transforming
+			// quaternion continuously accumulates floating point error and
+			// can end up being overflow. See #15335
+			this.bone.quaternion.copy( thQ2.multiply( thQ3 ).normalize() );
 
 			manager.freeThreeQuaternion( thQ );
 			manager.freeThreeQuaternion( thQ2 );
@@ -1072,19 +1070,24 @@ THREE.MMDPhysics = ( function () {
 
 			var manager = this.manager;
 
-			var tr = this.body.getCenterOfMassTransform();
-			var origin = tr.getOrigin();
-			
-			var matrixInv = manager.allocThreeMatrix4();
-			matrixInv.copy( this.bone.parent.matrixWorld ).getInverse( matrixInv );
-			
-			var pos = manager.allocThreeVector3();
-			pos.set( origin.x(), origin.y(), origin.z() ).applyMatrix4( matrixInv );
-
-			this.bone.position.copy( pos );
-
-			manager.freeThreeVector3( pos );
-			manager.freeThreeMatrix4( matrixInv );
+			var tr = this._getWorldTransformForBone();
+
+			var thV = manager.allocThreeVector3();
+
+			var o = manager.getOrigin( tr );
+			thV.set( o.x(), o.y(), o.z() );
+
+			if ( this.bone.parent ) {
+
+				this.bone.parent.worldToLocal( thV );
+
+			}
+
+			this.bone.position.copy( thV );
+
+			manager.freeThreeVector3( thV );
+
+			manager.freeTransform( tr );
 
 		}
 

+ 11 - 21
examples/js/controls/PointerLockControls.js

@@ -10,14 +10,7 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 	this.domElement = domElement || document.body;
 	this.isLocked = false;
 
-	camera.rotation.set( 0, 0, 0 );
-
-	var pitchObject = new THREE.Object3D();
-	pitchObject.add( camera );
-
-	var yawObject = new THREE.Object3D();
-	yawObject.position.y = 10;
-	yawObject.add( pitchObject );
+	var euler = new THREE.Euler( 0, 0, 0, 'YXZ' );
 
 	var PI_2 = Math.PI / 2;
 
@@ -28,10 +21,14 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
 		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
 
-		yawObject.rotation.y -= movementX * 0.002;
-		pitchObject.rotation.x -= movementY * 0.002;
+		euler.setFromQuaternion( camera.quaternion );
+
+		euler.y -= movementX * 0.002;
+		euler.x -= movementY * 0.002;
+
+		euler.x = Math.max( - PI_2, Math.min( PI_2, euler.x ) );
 
-		pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
+		camera.quaternion.setFromEuler( euler );
 
 	}
 
@@ -81,26 +78,19 @@ THREE.PointerLockControls = function ( camera, domElement ) {
 
 	};
 
-	this.getObject = function () {
+	this.getObject = function () { // retaining this method for backward compatibility
 
-		return yawObject;
+		return camera;
 
 	};
 
 	this.getDirection = function () {
 
-		// assumes the camera itself is not rotated
-
 		var direction = new THREE.Vector3( 0, 0, - 1 );
-		var rotation = new THREE.Euler( 0, 0, 0, 'YXZ' );
 
 		return function ( v ) {
 
-			rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 );
-
-			v.copy( direction ).applyEuler( rotation );
-
-			return v;
+			return v.copy( direction ).applyQuaternion( camera.quaternion );
 
 		};
 

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

@@ -128,6 +128,13 @@ THREE.TransformControls = function ( camera, domElement ) {
 		domElement.removeEventListener( "touchcancel", onPointerUp );
 		domElement.removeEventListener( "touchleave", onPointerUp );
 
+		this.traverse( function ( child ) {
+
+			if ( child.geometry ) child.geometry.dispose();
+			if ( child.material ) child.material.dispose();
+
+		} );
+
 	};
 
 	// Set current object
@@ -981,6 +988,7 @@ THREE.TransformControlsGizmo = function () {
 				var tempGeometry = object.geometry.clone();
 				tempGeometry.applyMatrix(object.matrix);
 				object.geometry = tempGeometry;
+				object.renderOrder = Infinity;
 
 				object.position.set( 0, 0, 0 );
 				object.rotation.set( 0, 0, 0 );

+ 47 - 5
examples/js/effects/OutlineEffect.js

@@ -3,6 +3,41 @@
  *
  * Reference: https://en.wikipedia.org/wiki/Cel_shading
  *
+ * API
+ *
+ * 1. Traditional
+ *
+ * var effect = new THREE.OutlineEffect( renderer );
+ *
+ * function render() {
+ *
+ * 	effect.render( scene, camera );
+ *
+ * }
+ *
+ * 2. VR compatible
+ *
+ * var effect = new THREE.OutlineEffect( renderer );
+ * var renderingOutline = false;
+ *
+ * scene.onAfterRender = function () {
+ *
+ * 	if ( renderingOutline ) return;
+ *
+ * 	renderingOutline = true;
+ *
+ * 	effect.renderOutline( scene, camera );
+ *
+ * 	renderingOutline = false;
+ *
+ * };
+ *
+ * function render() {
+ *
+ * 	renderer.render( scene, camera );
+ *
+ * }
+ *
  * // How to set default outline parameters
  * new THREE.OutlineEffect( renderer, {
  * 	defaultThickness: 0.01,
@@ -66,9 +101,9 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 	};
 
 	var uniformsChunk = {
-		outlineThickness: { type: "f", value: defaultThickness },
-		outlineColor: { type: "c", value: defaultColor },
-		outlineAlpha: { type: "f", value: defaultAlpha }
+		outlineThickness: { value: defaultThickness },
+		outlineColor: { value: defaultColor },
+		outlineAlpha: { value: defaultAlpha }
 	};
 
 	var vertexShaderChunk = [
@@ -435,10 +470,17 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 		var currentAutoClear = renderer.autoClear;
 		renderer.autoClear = this.autoClear;
 
-		// 1. render normally
 		renderer.render( scene, camera );
 
-		// 2. render outline
+		renderer.autoClear = currentAutoClear;
+
+		this.renderOutline( scene, camera );
+
+	};
+
+	this.renderOutline = function ( scene, camera ) {
+
+		var currentAutoClear = renderer.autoClear;
 		var currentSceneAutoUpdate = scene.autoUpdate;
 		var currentSceneBackground = scene.background;
 		var currentShadowMapEnabled = renderer.shadowMap.enabled;

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

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

File diff suppressed because it is too large
+ 7 - 3
examples/js/libs/ammo.js


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

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

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

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

+ 5 - 3
examples/js/loaders/KTXLoader.js

@@ -137,13 +137,15 @@ var KhronosTextureContainer = ( function () {
 		for ( var level = 0; level < mipmapCount; level ++ ) {
 
 			var imageSize = new Int32Array( this.arrayBuffer, dataOffset, 1 )[ 0 ]; // size per face, since not supporting array cubemaps
+			dataOffset += 4; // size of the image + 4 for the imageSize field
+			
 			for ( var face = 0; face < this.numberOfFaces; face ++ ) {
 
-				var byteArray = new Uint8Array( this.arrayBuffer, dataOffset + 4, imageSize );
+				var byteArray = new Uint8Array( this.arrayBuffer, dataOffset, imageSize );
 
 				mipmaps.push( { "data": byteArray, "width": width, "height": height } );
-
-				dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+				
+				dataOffset += imageSize;
 				dataOffset += 3 - ( ( imageSize + 3 ) % 4 ); // add padding for odd sized image
 
 			}

+ 201 - 75
examples/js/loaders/LDrawLoader.js

@@ -271,7 +271,7 @@ THREE.LDrawLoader = ( function () {
 
 			}, onProgress, onError );
 
-			function processObject( text, onProcessed ) {
+			function processObject( text, onProcessed, subobject ) {
 
 				var parseScope = scope.newParseScopeLevel();
 				parseScope.url = url;
@@ -280,13 +280,20 @@ THREE.LDrawLoader = ( function () {
 
 				// Add to cache
 				var currentFileName = parentParseScope.currentFileName;
+				if ( currentFileName !== null ) {
+
+					currentFileName = parentParseScope.currentFileName.toLowerCase();
+
+				}
+
 				if ( scope.subobjectCache[ currentFileName ] === undefined ) {
 
 					scope.subobjectCache[ currentFileName ] = text;
 
-
 				}
 
+				parseScope.inverted = subobject !== undefined ? subobject.inverted : false;
+
 				// Parse the object (returns a THREE.Group)
 				var objGroup = scope.parse( text );
 
@@ -295,37 +302,39 @@ THREE.LDrawLoader = ( function () {
 				parseScope.numSubobjects = parseScope.subobjects.length;
 				parseScope.subobjectIndex = 0;
 
-				if ( parseScope.numSubobjects > 0 ) {
-
-					// Load the first subobject
-					var subobjectGroup = loadSubobject( parseScope.subobjects[ 0 ], true );
+				var finishedCount = 0;
+				onSubobjectFinish();
 
-					// Optimization for loading pack: If subobjects are obtained from cache, keep loading them iteratively rather than recursively
-					if ( subobjectGroup ) {
+				return objGroup;
 
-						while ( subobjectGroup && parseScope.subobjectIndex < parseScope.numSubobjects - 1 ) {
+				function onSubobjectFinish() {
 
-							subobjectGroup = loadSubobject( parseScope.subobjects[ ++ parseScope.subobjectIndex ], true );
+					finishedCount ++;
 
-						}
+					if ( finishedCount === parseScope.subobjects.length + 1 ) {
 
-						if ( subobjectGroup ) {
+						finalizeObject();
 
-							finalizeObject();
+					} else {
 
-						}
+						// Once the previous subobject has finished we can start processing the next one in the list.
+						// The subobject processing shares scope in processing so it's important that they be loaded serially
+						// to avoid race conditions.
+						// Promise.resolve is used as an approach to asynchronously schedule a task _before_ this frame ends to
+						// avoid stack overflow exceptions when loading many subobjects from the cache. RequestAnimationFrame
+						// will work but causes the load to happen after the next frame which causes the load to take significantly longer.
+						var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
+						Promise.resolve().then( function () {
 
-					}
+							loadSubobject( subobject );
 
-				} else {
+						} );
+						parseScope.subobjectIndex ++;
 
-					// No subobjects, finish object
-					finalizeObject();
+					}
 
 				}
 
-				return objGroup;
-
 				function finalizeObject() {
 
 					if ( ! scope.separateObjects && ! parentParseScope.isFromParse ) {
@@ -355,7 +364,7 @@ THREE.LDrawLoader = ( function () {
 
 				}
 
-				function loadSubobject( subobject, sync ) {
+				function loadSubobject( subobject ) {
 
 					parseScope.mainColourCode = subobject.material.userData.code;
 					parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
@@ -369,16 +378,15 @@ THREE.LDrawLoader = ( function () {
 					}
 
 					// If subobject was cached previously, use the cached one
-					var cached = scope.subobjectCache[ subobject.originalFileName ];
+					var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
 					if ( cached ) {
 
-						var subobjectGroup = processObject( cached, sync ? undefined : onSubobjectLoaded );
-						if ( sync ) {
+						processObject( cached, function ( subobjectGroup ) {
 
-							addSubobject( subobject, subobjectGroup );
-							return subobjectGroup;
+							onSubobjectLoaded( subobjectGroup, subobject );
+							onSubobjectFinish();
 
-						}
+						}, subobject );
 
 						return;
 
@@ -438,37 +446,36 @@ THREE.LDrawLoader = ( function () {
 							// All location possibilities have been tried, give up loading this object
 							console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
 
-							// Try to read the next subobject
-							parseScope.subobjectIndex ++;
+							return;
 
-							if ( parseScope.subobjectIndex >= parseScope.numSubobjects ) {
+					}
 
-								// All subojects have been loaded. Finish parent object
-								scope.removeScopeLevel();
-								onProcessed( objGroup );
+					subobject.locationState = newLocationState;
+					subobject.url = subobjectURL;
 
-							} else {
+					// Load the subobject
+					// Use another file loader here so we can keep track of the subobject information
+					// and use it when processing the next model.
+					var fileLoader = new THREE.FileLoader( scope.manager );
+					fileLoader.setPath( scope.path );
+					fileLoader.load( subobjectURL, function ( text ) {
 
-								// Load next subobject
-								loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
+						processObject( text, function ( subobjectGroup ) {
 
-							}
+							onSubobjectLoaded( subobjectGroup, subobject );
+							onSubobjectFinish();
 
-							return;
+						}, subobject );
 
-					}
+					}, undefined, function ( err ) {
 
-					subobject.locationState = newLocationState;
-					subobject.url = subobjectURL;
+						onSubobjectError( err, subobject );
 
-					// Load the subobject
-					scope.load( subobjectURL, onSubobjectLoaded, undefined, onSubobjectError );
+					}, subobject );
 
 				}
 
-				function onSubobjectLoaded( subobjectGroup ) {
-
-					var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
+				function onSubobjectLoaded( subobjectGroup, subobject ) {
 
 					if ( subobjectGroup === null ) {
 
@@ -481,20 +488,6 @@ THREE.LDrawLoader = ( function () {
 					// Add the subobject just loaded
 					addSubobject( subobject, subobjectGroup );
 
-					// Proceed to load the next subobject, or finish the parent object
-
-					parseScope.subobjectIndex ++;
-
-					if ( parseScope.subobjectIndex < parseScope.numSubobjects ) {
-
-						loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
-
-					} else {
-
-						finalizeObject();
-
-					}
-
 				}
 
 				function addSubobject( subobject, subobjectGroup ) {
@@ -512,10 +505,10 @@ THREE.LDrawLoader = ( function () {
 
 				}
 
-				function onSubobjectError( err ) {
+				function onSubobjectError( err, subobject ) {
 
 					// Retry download from a different default possible location
-					loadSubobject( parseScope.subobjects[ parseScope.subobjectIndex ] );
+					loadSubobject( subobject );
 
 				}
 
@@ -587,6 +580,7 @@ THREE.LDrawLoader = ( function () {
 				subobjects: null,
 				numSubobjects: 0,
 				subobjectIndex: 0,
+				inverted: false,
 
 				// Current subobject
 				currentFileName: null,
@@ -897,9 +891,6 @@ THREE.LDrawLoader = ( function () {
 
 			}
 
-			// BFC (Back Face Culling) LDraw language meta extension is not implemented, so set all materials double-sided:
-			material.side = THREE.DoubleSide;
-
 			material.transparent = isTransparent;
 			material.opacity = alpha;
 
@@ -1035,6 +1026,11 @@ THREE.LDrawLoader = ( function () {
 
 			}
 
+			var bfcCertified = false;
+			var bfcCCW = true;
+			var bfcInverted = false;
+			var bfcCull = true;
+
 			// Parse all line commands
 			for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
 
@@ -1047,7 +1043,7 @@ THREE.LDrawLoader = ( function () {
 					if ( line.startsWith( '0 FILE ' ) ) {
 
 						// Save previous embedded file in the cache
-						this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
+						this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
 
 						// New embedded text file
 						currentEmbeddedFileName = line.substring( 7 );
@@ -1137,6 +1133,58 @@ THREE.LDrawLoader = ( function () {
 										currentEmbeddedFileName = lp.getRemainingString();
 										currentEmbeddedText = '';
 
+										bfcCertified = false;
+										bfcCCW = true;
+
+									}
+
+									break;
+
+								case 'BFC':
+
+									// Changes to the backface culling state
+									while ( ! lp.isAtTheEnd() ) {
+
+										var token = lp.getToken();
+
+										switch ( token ) {
+
+											case 'CERTIFY':
+											case 'NOCERTIFY':
+
+												bfcCertified = token === 'CERTIFY';
+												bfcCCW = true;
+
+												break;
+
+											case 'CW':
+											case 'CCW':
+
+												bfcCCW = token === 'CCW';
+
+												break;
+
+											case 'INVERTNEXT':
+
+												bfcInverted = true;
+
+												break;
+
+											case 'CLIP':
+											case 'NOCLIP':
+
+											  bfcCull = token === 'CLIP';
+
+												break;
+
+											default:
+
+												console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
+
+												break;
+
+										}
+
 									}
 
 									break;
@@ -1198,6 +1246,14 @@ THREE.LDrawLoader = ( function () {
 
 						}
 
+						// If the scale of the object is negated then the triangle winding order
+						// needs to be flipped.
+						if ( scope.separateObjects === false && matrix.determinant() < 0 ) {
+
+							bfcInverted = ! bfcInverted;
+
+						}
+
 						subobjects.push( {
 							material: material,
 							matrix: matrix,
@@ -1205,9 +1261,12 @@ THREE.LDrawLoader = ( function () {
 							originalFileName: fileName,
 							locationState: LDrawLoader.FILE_LOCATION_AS_IS,
 							url: null,
-							triedLowerCase: false
+							triedLowerCase: false,
+							inverted: bfcInverted !== currentParseScope.inverted
 						} );
 
+						bfcInverted = false;
+
 						break;
 
 					// Line type 2: Line segment
@@ -1229,14 +1288,45 @@ THREE.LDrawLoader = ( function () {
 
 						var material = parseColourCode( lp );
 
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+
+						} else {
+
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
+
 						triangles.push( {
 							material: material,
 							colourCode: material.userData.code,
-							v0: parseVector( lp ),
-							v1: parseVector( lp ),
-							v2: parseVector( lp )
+							v0: v0,
+							v1: v1,
+							v2: v2
 						} );
 
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1
+							} );
+
+						}
+
 						break;
 
 					// Line type 4: Quadrilateral
@@ -1244,10 +1334,26 @@ THREE.LDrawLoader = ( function () {
 
 						var material = parseColourCode( lp );
 
-						var v0 = parseVector( lp );
-						var v1 = parseVector( lp );
-						var v2 = parseVector( lp );
-						var v3 = parseVector( lp );
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2, v3;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+							v3 = parseVector( lp );
+
+						} else {
+
+							v3 = parseVector( lp );
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
 
 						triangles.push( {
 							material: material,
@@ -1265,6 +1371,26 @@ THREE.LDrawLoader = ( function () {
 							v2: v3
 						} );
 
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1
+							} );
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v3,
+								v2: v2
+							} );
+
+						}
+
 						break;
 
 					// Line type 5: Optional line
@@ -1282,7 +1408,7 @@ THREE.LDrawLoader = ( function () {
 
 			if ( parsingEmbeddedFiles ) {
 
-				this.subobjectCache[ currentEmbeddedFileName ] = currentEmbeddedText;
+				this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
 
 			}
 

+ 167 - 25
examples/js/loaders/LWOLoader.js

@@ -1,12 +1,12 @@
 /**
  * @author Lewy Blue https://github.com/looeee
  *
- * Load files in LWO3 format
+ * Load files in LWO3 and LWO2 format
  *
  * LWO3 format specification:
  * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
  *
- * LWO2 format specification (not tested, however the loader should be largely backwards compatible)
+ * LWO2 format specification:
  * 	http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
  *
  */
@@ -291,7 +291,15 @@ THREE.LWOLoader = ( function () {
 
 			for ( var name in lwoTree.materials ) {
 
-				materials.push( this.parseMaterial( lwoTree.materials[ name ], name, lwoTree.textures ) );
+				if ( lwoTree.format === 'LWO3' ) {
+
+					materials.push( this.parseMaterial( lwoTree.materials[ name ], name, lwoTree.textures ) );
+
+				} else if ( lwoTree.format === 'LWO2' ) {
+
+					materials.push( this.parseMaterialLwo2( lwoTree.materials[ name ], name, lwoTree.textures ) );
+
+				}
 
 			}
 
@@ -326,6 +334,20 @@ THREE.LWOLoader = ( function () {
 
 		},
 
+		parseMaterialLwo2( materialData, name, textures ) {
+
+			var params = {
+				name: name,
+				side: this.getSide( materialData.attributes ),
+				flatShading: this.getSmooth( materialData.attributes ),
+			};
+
+			var attributes = this.parseAttributes( materialData.attributes, {} );
+			params = Object.assign( params, attributes );
+			return new THREE[ 'MeshPhongMaterial' ]( params );
+
+		},
+
 		// Note: converting from left to right handed coords by switching x -> -x in vertices, and
 		// then switching mat FrontSide -> BackSide
 		// NB: this means that THREE.FrontSide and THREE.BackSide have been switched!
@@ -404,7 +426,7 @@ THREE.LWOLoader = ( function () {
 
 			var maps = {};
 
-			for ( name in textureNodes ) {
+			for ( var name in textureNodes ) {
 
 				var node = textureNodes[ name ];
 				var path = node.fileName;
@@ -656,7 +678,7 @@ THREE.LWOLoader = ( function () {
 		// In this case, we'll strip out everything and load 'bumpMap.png' from the same directory as the model
 		cleanPath( path ) {
 
-			if ( path.indexOf( 'Images' ) === 0 ) return './' + path;
+			if ( path.toLowerCase().indexOf( 'images' ) === 0 ) return './' + path;
 			return path.split( '/' ).pop().split( '\\' ).pop();
 
 		},
@@ -744,7 +766,17 @@ THREE.LWOLoader = ( function () {
 
 					);
 
-				} else if ( dim > 4 ) console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
+				} else if ( dim > 4 ) {
+
+					for ( var k = 1; k < dim - 1; k ++ ) {
+
+						remappedIndices.push( indices[ i ], indices[ i + k ], indices[ i + k + 1 ] );
+
+					}
+
+					console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
+
+				}
 
 				i += dim;
 
@@ -850,7 +882,16 @@ THREE.LWOLoader = ( function () {
 
 					remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
 
-				} // ignore > 4 for now
+				} else {
+
+					 // ignore > 4 for now
+					for ( var k = 0; k < dim - 2; k ++ ) {
+
+						remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
+
+					}
+
+				}
 
 			} );
 
@@ -1001,6 +1042,13 @@ THREE.LWOLoader = ( function () {
 
 			var blockID = this.reader.getIDTag();
 			var length = this.reader.getUint32(); // size of data in bytes
+			if ( this.tree.format === 'LWO2' && length > this.reader.dv.byteLength - this.reader.offset ) {
+
+				this.reader.offset -= 4;
+				length = this.reader.getUint16();
+
+			}
+
 
 			// Data types may be found in either LWO2 OR LWO3 spec
 			switch ( blockID ) {
@@ -1080,7 +1128,6 @@ THREE.LWOLoader = ( function () {
 				case 'NPLA':
 				case 'VERS':
 				case 'ENUM':
-				case 'FLAG':
 				case 'TAG ':
 
 				// Car Material CHUNKS
@@ -1096,8 +1143,22 @@ THREE.LWOLoader = ( function () {
 					this.reader.skip( length );
 					break;
 
+				case 'FLAG':
+					if ( this.tree.format === 'LWO2' ) {
+
+						this.reader.skip( 4 ); // not suported
+
+					} else {
+
+						this.reader.skip( length );
+
+					}
+					break;
 				// Skipped LWO2 chunks
 				case 'DIFF': // diffuse level, may be necessary to modulate COLR with this
+					this.currentSurface.diffusePower = this.reader.getFloat32();
+					this.reader.skip( 2 );
+					break;
 				case 'TRNL':
 				case 'REFL':
 				case 'GLOS':
@@ -1113,12 +1174,24 @@ THREE.LWOLoader = ( function () {
 				case 'GLOW':
 				case 'LINE':
 				case 'ALPH':
-				case 'LINE':
 				case 'VCOL':
 				case 'ENAB':
 					this.reader.skip( length );
 					break;
+				case 'SURF':
+					if ( this.tree.format === 'LWO2' ) {
 
+						this.parseSurfaceLwo2( length );
+
+					}
+					break;
+				case 'CLIP':
+					if ( this.tree.format === 'LWO2' ) {
+
+						this.parseClipLwo2( length );
+
+					}
+					break;
 				// Texture node chunks (not in spec)
 				case 'IPIX': // usePixelBlending
 				case 'IMIP': // useMipMaps
@@ -1270,13 +1343,10 @@ THREE.LWOLoader = ( function () {
 					this.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
 					break;
 
-				case 'ENAB':
-					this.currentForm.enabled = this.reader.getUint16();
-					break;
-
 				// LWO2: Basic Surface Parameters
 				case 'COLR':
-					this.currentSurface.attributes.color = this.reader.getFloat32Array( 3 );
+					this.currentSurface.attributes.Color = {};
+					this.currentSurface.attributes.Color.value = this.reader.getFloat32Array( 3 );
 					this.reader.skip( 2 ); // VX: envelope
 					break;
 
@@ -1290,11 +1360,6 @@ THREE.LWOLoader = ( function () {
 					this.reader.skip( 2 );
 					break;
 
-				case 'REFL':
-					this.currentSurface.attributes.reflectivity = this.reader.getFloat32();
-					this.reader.skip( 2 );
-					break;
-
 				case 'TRAN':
 					this.currentSurface.attributes.opacity = this.reader.getFloat32();
 					this.reader.skip( 2 );
@@ -1323,7 +1388,15 @@ THREE.LWOLoader = ( function () {
 					break;
 
 				case 'IMAP':
-					this.currentSurface.attributes.imageMapIndex = this.reader.getUint32();
+					if ( this.tree.format === 'LWO2' ) {
+
+						this.reader.skip( 2 );
+
+					} else {
+
+						this.currentSurface.attributes.imageMapIndex = this.reader.getUint32();
+
+					}
 					break;
 
 				case 'IUVI': // uv channel name
@@ -1337,6 +1410,11 @@ THREE.LWOLoader = ( function () {
 					this.currentNode.heightWrappingMode = this.reader.getUint32();
 					break;
 
+				// LWO2 USE
+				case 'BLOK':
+					// skip
+					break;
+
 				default:
 					this.parseUnknownCHUNK( blockID, length );
 
@@ -1420,6 +1498,10 @@ THREE.LWOLoader = ( function () {
 					this.parseTextureNodeAttribute( type );
 					break;
 
+				case 'LWO2':
+					this.tree.format = type;
+					break;
+
 				case 'LWO3':
 					this.tree.format = type;
 					break;
@@ -1431,7 +1513,15 @@ THREE.LWOLoader = ( function () {
 					// CLIP FORM AND SUB FORMS
 
 				case 'CLIP':
-					this.parseClip( length );
+					if ( this.tree.format === 'LWO2' ) {
+
+						this.parseForm( length );
+
+					} else {
+
+						this.parseClip( length );
+
+					}
 					break;
 
 				case 'STIL':
@@ -1466,10 +1556,6 @@ THREE.LWOLoader = ( function () {
 					this.parseSubNode( length );
 					break;
 
-				case 'NNDS':
-					this.setupForm( 'nodes', length );
-					break;
-
 				case 'ATTR': // BSDF Node Attributes
 				case 'SATR': // Standard Node Attributes
 					this.setupForm( 'attributes', length );
@@ -1574,6 +1660,29 @@ THREE.LWOLoader = ( function () {
 
 			var name = this.reader.getString();
 
+			var surface = {
+				attributes: {}, // LWO2 style non-node attributes will go here
+				connections: {},
+				name: name,
+				inputName: name,
+				nodes: {},
+				source: this.reader.getString(),
+			};
+
+			this.tree.materials[ name ] = surface;
+			this.currentSurface = surface;
+
+			this.parentForm = this.tree.materials;
+			this.currentForm = surface;
+			this.currentFormEnd = this.reader.offset + length;
+
+		},
+
+		parseSurfaceLwo2( length ) {
+
+			var firstOffset = this.reader.offset;
+			var name = this.reader.getString();
+
 			var surface = {
 				attributes: {}, // LWO2 style non-node attributes will go here
 				connections: {},
@@ -1771,6 +1880,39 @@ THREE.LWOLoader = ( function () {
 
 		},
 
+		parseClipLwo2( length ) {
+
+			var texture = {
+				index: this.reader.getUint32(),
+				fileName: ""
+			};
+
+			var readed = 4;
+			// seach STIL block
+			while ( true ) {
+
+				var tag = this.reader.getIDTag();
+				var n_length = this.reader.getUint16();
+				if ( tag === 'STIL' ) {
+
+					texture.fileName = this.reader.getString();
+					break;
+
+				}
+				readed += 4 + n_length;
+				if ( n_length >= length ) {
+
+					break;
+
+				}
+
+			}
+
+			this.tree.textures.push( texture );
+			this.currentForm = texture;
+
+		},
+
 		parseImage() {
 
 			this.reader.skip( 8 ); // unknown

+ 3 - 0
examples/js/loaders/MMDLoader.js

@@ -1359,6 +1359,9 @@ THREE.MMDLoader = ( function () {
 
 					t.image = scope._getRotatedImage( t.image );
 
+					t.magFilter = THREE.NearestFilter;
+					t.minFilter = THREE.NearestFilter;
+
 				}
 
 				t.flipY = false;

+ 18 - 5
examples/js/loaders/SVGLoader.js

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

+ 21 - 5
examples/js/loaders/VRMLLoader.js

@@ -512,7 +512,9 @@ THREE.VRMLLoader.prototype = {
 
 					}
 
-					node[ fieldName ] = property;
+					// VRMLLoader does not support text so it can't process the "string" property yet
+
+					if ( fieldName !== 'string' ) node[ fieldName ] = property;
 
 				}
 
@@ -825,6 +827,16 @@ THREE.VRMLLoader.prototype = {
 
 						parent.geometry = new THREE.SphereBufferGeometry( data.radius );
 
+					} else if ( data.nodeType === 'IndexedLineSet' ) {
+
+						console.warn( 'THREE.VRMLLoader: IndexedLineSet not supported yet.' );
+						parent.parent.remove( parent ); // since the loader is not able to parse the geometry, remove the respective 3D object
+
+					} else if ( data.nodeType === 'Text' ) {
+
+						console.warn( 'THREE.VRMLLoader: Text not supported yet.' );
+						parent.parent.remove( parent );
+
 					} else if ( data.nodeType === 'IndexedFaceSet' ) {
 
 						var geometry = new THREE.BufferGeometry();
@@ -1244,13 +1256,13 @@ THREE.VRMLLoader.prototype = {
 
 		// some lines do not have breaks
 
-		for ( var i = lines.length - 1; i > - 1; i -- ) {
-
-			var line = lines[ i ];
+		for ( var i = lines.length - 1; i > 0; i -- ) {
 
 			// The # symbol indicates that all subsequent text, until the end of the line is a comment,
 			// and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html)
-			line = line.replace( /(#.*)/, '' );
+			lines[ i ] = lines[ i ].replace( /(#.*)/, '' );
+
+			var line = lines[ i ];
 
 			// split lines with {..{ or {..[ - some have both
 			if ( /{.*[{\[]/.test( line ) ) {
@@ -1270,6 +1282,8 @@ THREE.VRMLLoader.prototype = {
 
 			}
 
+			line = lines[ i ];
+
 			if ( /}.*}/.test( line ) ) {
 
 				// split lines with }..}
@@ -1280,6 +1294,8 @@ THREE.VRMLLoader.prototype = {
 
 			}
 
+			line = lines[ i ];
+
 			if ( /^\b[^\s]+\b$/.test( line.trim() ) ) {
 
 				// prevent lines with single words like "coord" or "geometry", see #12209

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

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

+ 12 - 3
examples/js/nodes/accessors/NormalNode.js

@@ -43,7 +43,8 @@ NormalNode.prototype.generate = function ( builder, output ) {
 
 		case NormalNode.LOCAL:
 
-			builder.requires.normal = true;
+			// to use vObjectNormal as vertex normal
+			//builder.requires.normal = true;
 
 			result = 'normal';
 
@@ -51,9 +52,17 @@ NormalNode.prototype.generate = function ( builder, output ) {
 
 		case NormalNode.WORLD:
 
-			builder.requires.worldNormal = true;
+			if ( builder.isShader( 'vertex' ) ) {
 
-			result = builder.isShader( 'vertex' ) ? '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz' : 'vWNormal';
+				return '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
+
+			} else {
+
+				builder.requires.worldNormal = true;
+
+				result = 'vWNormal';
+
+			}
 
 			break;
 

+ 20 - 4
examples/js/nodes/accessors/PositionNode.js

@@ -59,17 +59,33 @@ PositionNode.prototype.generate = function ( builder, output ) {
 
 		case PositionNode.LOCAL:
 
-			builder.requires.position = true;
+			if ( builder.isShader( 'vertex' ) ) {
 
-			result = builder.isShader( 'vertex' ) ? 'transformed' : 'vPosition';
+				result = 'transformed';
+
+			} else {
+
+				builder.requires.position = true;
+
+				result = 'vPosition';
+
+			}
 
 			break;
 
 		case PositionNode.WORLD:
 
-			builder.requires.worldPosition = true;
+			if ( builder.isShader( 'vertex' ) ) {
+
+				return '( modelMatrix * vec4( transformed, 1.0 ) ).xyz';
+
+			} else {
+
+				builder.requires.worldPosition = true;
+
+				result = 'vWPosition';
 
-			result = 'vWPosition';
+			}
 
 			break;
 

+ 7 - 4
examples/js/nodes/accessors/ResolutionNode.js

@@ -8,6 +8,8 @@ function ResolutionNode() {
 
 	Vector2Node.call( this );
 
+	this.size = new THREE.Vector2();
+
 }
 
 ResolutionNode.prototype = Object.create( Vector2Node.prototype );
@@ -18,11 +20,12 @@ ResolutionNode.prototype.updateFrame = function ( frame ) {
 
 	if ( frame.renderer ) {
 
-		var size = frame.renderer.getSize(),
-			pixelRatio = frame.renderer.getPixelRatio();
+		frame.renderer.getSize( this.size );
+
+		var pixelRatio = frame.renderer.getPixelRatio();
 
-		this.x = size.width * pixelRatio;
-		this.y = size.height * pixelRatio;
+		this.x = this.size.width * pixelRatio;
+		this.y = this.size.height * pixelRatio;
 
 	} else {
 

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

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

+ 8 - 8
examples/js/shaders/DepthLimitedBlurShader.js

@@ -5,14 +5,14 @@ THREE.DepthLimitedBlurShader = {
 		'PERSPECTIVE_CAMERA': 1
 	},
 	uniforms: {
-		'tDiffuse': { type: 't', value: null },
-		'size': { type: 'v2', value: new THREE.Vector2( 512, 512 ) },
-		'sampleUvOffsets': { type: 'v2v', value: [ new THREE.Vector2( 0, 0 ) ] },
-		'sampleWeights': { type: '1fv', value: [ 1.0 ] },
-		'tDepth': { type: 't', value: null },
-		'cameraNear': { type: 'f', value: 10 },
-		'cameraFar': { type: 'f', value: 1000 },
-		'depthCutoff': { type: 'f', value: 10 },
+		'tDiffuse': { value: null },
+		'size': { value: new THREE.Vector2( 512, 512 ) },
+		'sampleUvOffsets': { value: [ new THREE.Vector2( 0, 0 ) ] },
+		'sampleWeights': { value: [ 1.0 ] },
+		'tDepth': { value: null },
+		'cameraNear': { value: 10 },
+		'cameraFar': { value: 1000 },
+		'depthCutoff': { value: 10 },
 	},
 	vertexShader: [
 		"#include <common>",

+ 5 - 5
examples/js/shaders/LuminosityHighPassShader.js

@@ -11,11 +11,11 @@ THREE.LuminosityHighPassShader = {
 
 	uniforms: {
 
-		"tDiffuse": { type: "t", value: null },
-		"luminosityThreshold": { type: "f", value: 1.0 },
-		"smoothWidth": { type: "f", value: 1.0 },
-		"defaultColor": { type: "c", value: new THREE.Color( 0x000000 ) },
-		"defaultOpacity":  { type: "f", value: 0.0 }
+		"tDiffuse": { value: null },
+		"luminosityThreshold": { value: 1.0 },
+		"smoothWidth": { value: 1.0 },
+		"defaultColor": { value: new THREE.Color( 0x000000 ) },
+		"defaultOpacity":  { value: 0.0 }
 
 	},
 

+ 17 - 17
examples/js/shaders/SAOShader.js

@@ -9,23 +9,23 @@ THREE.SAOShader = {
 	},
 	uniforms: {
 
-		'tDepth': { type: 't', value: null },
-		'tDiffuse': { type: 't', value: null },
-		'tNormal': { type: 't', value: null },
-		'size': { type: 'v2', value: new THREE.Vector2( 512, 512 ) },
-
-		'cameraNear': { type: 'f', value: 1 },
-		'cameraFar': { type: 'f', value: 100 },
-		'cameraProjectionMatrix': { type: 'm4', value: new THREE.Matrix4() },
-		'cameraInverseProjectionMatrix': { type: 'm4', value: new THREE.Matrix4() },
-
-		'scale': { type: 'f', value: 1.0 },
-		'intensity': { type: 'f', value: 0.1 },
-		'bias': { type: 'f', value: 0.5 },
-
-		'minResolution': { type: 'f', value: 0.0 },
-		'kernelRadius': { type: 'f', value: 100.0 },
-		'randomSeed': { type: 'f', value: 0.0 }
+		'tDepth': { value: null },
+		'tDiffuse': { value: null },
+		'tNormal': { value: null },
+		'size': { value: new THREE.Vector2( 512, 512 ) },
+
+		'cameraNear': { value: 1 },
+		'cameraFar': { value: 100 },
+		'cameraProjectionMatrix': { value: new THREE.Matrix4() },
+		'cameraInverseProjectionMatrix': { value: new THREE.Matrix4() },
+
+		'scale': { value: 1.0 },
+		'intensity': { value: 0.1 },
+		'bias': { value: 0.5 },
+
+		'minResolution': { value: 0.0 },
+		'kernelRadius': { value: 100.0 },
+		'randomSeed': { value: 0.0 }
 	},
 	vertexShader: [
 		"varying vec2 vUv;",

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

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

+ 7 - 0
examples/jsm/exporters/ColladaExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from '../../../src/Three';
+
+export class ColladaExporter {
+	constructor();
+
+	parse(object: Object3D, onDone: (res: any) => void, options: object): null;
+}

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

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

+ 3 - 3
examples/jsm/exporters/GLTFExporter.d.ts

@@ -1,7 +1,7 @@
-import { Object3D } from '../../../src/Three';
+import {Object3D} from '../../../src/Three';
 
 export class GLTFExporter {
-  constructor();
+	constructor();
 
-  parse(input: Object3D, onCompleted: (gltf: object) => void, options: object): null;
+	parse(input: Object3D, onCompleted: (gltf: object) => void, options: object): null;
 }

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

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

+ 7 - 0
examples/jsm/exporters/MMDExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from "../../..";
+
+export class MMDExporter {
+	constructor();
+
+	parseVpd(skin: Object3D, outputShiftJis: boolean, useOriginalBones: boolean): [] | Uint8Array
+}

+ 7 - 0
examples/jsm/exporters/OBJExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from '../../../src/Three';
+
+export class OBJExporter {
+	constructor();
+
+	parse(object: Object3D): string;
+}

+ 7 - 0
examples/jsm/exporters/PLYExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from "../../..";
+
+export class PLYExporter {
+	constructor();
+
+	parse(object: Object3D, onDone: (res: any) => void, options: object): null;
+}

+ 7 - 0
examples/jsm/exporters/STLExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from "../../..";
+
+export class STLExporter {
+	constructor();
+
+	parse(scene: Object3D, options: {});
+}

+ 7 - 0
examples/jsm/exporters/TypedGeometryExporter.d.ts

@@ -0,0 +1,7 @@
+import {Object3D} from "../../..";
+
+export class TypedGeometryExporterD {
+	constructor();
+
+	parse(scene: Object3D, options: {}): DataView | string;
+}

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

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

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

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

+ 1 - 6
examples/jsm/pmrem/PMREMCubeUVPacker.d.ts

@@ -1,9 +1,4 @@
-import {
-  CubeTexture,
-  Renderer,
-  ShaderMaterial,
-  WebGLRenderTarget
-} from '../../../src/Three';
+import {CubeTexture, Renderer, WebGLRenderTarget} from '../../../src/Three';
 
 export class PMREMCubeUVPacker {
   CubeUVRenderTarget:WebGLRenderTarget;

+ 5 - 0
examples/jsm/utils/MathUtils.d.ts

@@ -0,0 +1,5 @@
+import {Quaternion} from "../../..";
+
+export namespace MathUtils {
+	export function setQuaternionFromProperEuler(q: Quaternion, a: number, b: number, c: number, order: string): void ;
+}

+ 8 - 0
examples/jsm/utils/ShadowMapViewer.d.ts

@@ -0,0 +1,8 @@
+import {Light} from "../../..";
+
+export class ShadowMapViewer {
+	constructor(light: Light)
+}
+
+
+

+ 32 - 0
examples/jsm/utils/SkeletonUtils.d.ts

@@ -0,0 +1,32 @@
+import {AnimationClip, Bone, Matrix4, Object3D, Skeleton, SkeletonHelper} from "../../..";
+
+export class SkeletonUtils {
+	retarget(target: Object3D | Skeleton,
+					 source: Object3D | Skeleton,
+					 options: {})
+
+	retargetClip(target: Skeleton | Object3D,
+							 source: Skeleton | Object3D,
+							 clip: AnimationClip,
+							 options: {}): AnimationClip;
+
+	getHelperFromSkeleton(skeleton: Skeleton): SkeletonHelper;
+
+	getSkeletonOffsets(target: Object3D | Skeleton,
+										 source: Object3D | Skeleton,
+										 options: {}): Matrix4[];
+
+	renameBones(skeleton: Skeleton, names: {}): any;
+
+	getBones(skeleton: Skeleton | Bone[]): Bone[];
+
+	getBoneByName(name: string, skeleton: Skeleton): Bone;
+
+	getNearestBone(bone: Bone, names: {}): Bone;
+
+	findBoneTrackData(name: string, tracks: any[]): {};
+
+	getEqualsBonesNames(skeleton: Skeleton, targetSkeleton: Skeleton);
+
+	clone(source: Skeleton): Skeleton;
+}

+ 58 - 0
examples/jsm/utils/TypedArrayUtils.d.ts

@@ -0,0 +1,58 @@
+export namespace TypedArrayUtils {
+	export function quicksortIP(arr: any[], eleSize: number, orderElement: number): any[];
+
+
+	export class Kdtree {
+		self: this;
+		root: Node;
+		private maxDepth: number;
+
+		constructor(points: Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Float32Array | Float64Array | Uint8ClampedArray, metric: (a: any, b: any) => number, eleSize: number);
+
+		getPointSet(points: any, pos: number);
+
+		buildTree(): Node;
+
+		getMaxDepth(): number;
+
+		nearest(point: [], maxNodes: number, maxDistance: number): any[];
+
+	}
+
+	export namespace Kdtree {
+		export class Node {
+			obj: any;
+			left: Node | null;
+			right: Node | null;
+			parent: Node;
+			depth: number;
+			pos: any;
+
+			constructor(obj: any, depth: number, parent: Node, pos: any)
+		}
+
+
+		export class BinaryHeap {
+			content: any[];
+			scoreFunction: () => any;
+
+			constructor(scoreFunction?: () => any);
+		}
+
+		export namespace BinaryHeap {
+			export function push(element: any): void;
+
+			export function pop(): any;
+
+			export function peek(): any;
+
+			export function remove(node: any): any;
+
+			export function size(): number;
+
+			export function bubbleUp(n: number): void;
+
+			export function sinkDown(n: number): void;
+		}
+	}
+}

+ 3 - 0
examples/jsm/utils/UVsDebug.d.ts

@@ -0,0 +1,3 @@
+import {Geometry} from "../../..";
+
+export function UVsDebug(geometry: Geometry, size: number): HTMLCanvasElement;

+ 1 - 2
examples/misc_controls_pointerlock.html

@@ -328,9 +328,8 @@
 						canJump = true;
 
 					}
-
 					controls.getObject().translateX( velocity.x * delta );
-					controls.getObject().translateY( velocity.y * delta );
+					controls.getObject().position.y += ( velocity.y * delta ); // new behavior
 					controls.getObject().translateZ( velocity.z * delta );
 
 					if ( controls.getObject().position.y < 10 ) {

+ 3 - 3
examples/misc_controls_transform.html

@@ -15,7 +15,7 @@
 				line-height: 30px;
 				overflow: hidden;
 			}
-			
+
 			#info {
 				position: absolute;
 				top: 0px; width: 100%;
@@ -23,7 +23,7 @@
 				z-index:100;
 				box-sizing: border-box;
 			}
-			
+
 			a {
 				color: white;
 			}
@@ -79,7 +79,7 @@
 				texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
 
 				var geometry = new THREE.BoxBufferGeometry( 200, 200, 200 );
-				var material = new THREE.MeshLambertMaterial( { map: texture } );
+				var material = new THREE.MeshLambertMaterial( { map: texture, transparent: true } );
 
 				orbit = new THREE.OrbitControls( camera, renderer.domElement );
 				orbit.update();

File diff suppressed because it is too large
+ 0 - 0
examples/models/json/pressure.json


BIN
examples/models/sea3d/morph.sea


BIN
examples/models/sea3d/morph.tjs.sea


BIN
examples/textures/alphaMap.jpg


BIN
examples/textures/equirectangular/pedestrian_overpass_2k.hdr


BIN
examples/textures/equirectangular/venice_sunset_2k.hdr


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